变量的三个属性——作用域、链接属性和存储类型

1.变量的声明和定义

1.1变量的声明

声明并不为变量开辟存储空间,声明只是为了向程序表明变量的类型和名字,在调用一个其它文件的函数或者全局变量的时候都需要进行声明;
在这里插入图片描述

变量声明方式是:说明符(一个或者多个关键字) + 声明表达式;

比如: int i , j , k;

在声明整形变量的时候,如果已经至少有了一个其他的说明符,关键字int可以进行省略;

比如:long a; 与long int a是等价的,signed int 与 int是等价的,unsigned int 与unsigned是等价的,如下表格1-1所述

等价的整形声明

int , signed intunsigned int, unsigned
short int ,short, signed short, signed short intunsigned short int, unsigned short
long int ,long ,signed long, signed long intunsigned long int,unsigned long
                                                            表1-1

可能有人发现表格中缺少了整形之中的char类型,这是因为表格之中的这些整形默认就是有符号的,而char类型在不同的编译器之中的取值是不同的,有可能是signed char也有可能是unsigned char;

1.2变量的定义

定义为变量开辟储存空间,并且还可以为变量指定初始值,程序中变量有且仅有一次定义,可以有多次声明;

定义也是声明,但是声明不是定义;

变量可以多次被声明,但是只可以被定义一次;就好比人的名字,上户口的名字叫做定义,你在山东告诉别人你叫张三,在山西也告诉别人你叫张三,这就是多次声明,而不是在广东你给自己取个名字叫李四,在广西又取名字叫来福,这就是重复定义了,如下图1-2所示;
在这里插入图片描述

2.作用域

2.1什么是作用域

当一个变量被声明后,这个变量并不总是有效的,这个变量名称的有效范围就叫做该变量的作用域,标识符的声明位置决定了它的作用域;

比如:全局变量的作用域是从声明开始直至文件尾,局部变量的作用域是从声明开始对应的代码块内;

编译器可以确认4种不同类型的作用域,文件作用域,代码块作用域,函数作用域以及函数原型作用域;

文件作用域:任何在所有代码块之外声明的标识符(变量,函数等名称)都具有文件作用域,即这些标识符从声明开始直至他们所处的源文件尾部都是可以被访问的。比如局部变量,函数名,通过头文件包含到其它文件的声明;

代码块作用域:位于一对花括号({})之内的所有语句构成一个代码块,即在代码块中声明的变量的作用域是从声明开始直至代码块结束;

这里就有点意思了:比如嵌套的代码块禁止使用同样的变量名,因为作用域重叠了。互相独立的代码块是可以使用相同名字的变量的,因为两个代码块的变量不可能同时存在,因此编译器可以让他们共享一个地址,因为两个互相独立的代码块,同名字的变量最多有一个处于活动之中;

函数作用域:只用于语句标签,即用于goto语句,简而言之:一个函数的所有语句标签必须唯一;

原型作用域:只适用于函数原型声明中的参数名,即函数形参的作用域只是在声明的语句之中。比如两个函数声明的形参可以相同,同时函数声明形参的名字并非必须的,如下图2-1所示
在这里插入图片描述

3.链接属性

3.1链接external(外部)、internal(内部)、none(无)

链接属性影响多文件中程序如何看待标识符;

external:具有external属性的标识符不论声明多少次位于几个源文件都表示同一个实体。即具有external属性的标识符具有跨文件属性;

internal:具有internal属性的标识符在同一个源文件内的所有声明都表示同一个实体。即具有internal属性的标识符不能跨文件访问;

none:没有连接属性的标识符,总是被当作单独的个体。即没有链接属性的标识符的多次声明都表示不同实体;

3.2 static和extern

这两个关键字用于在声明之中修改标识符的链接属性;

static只能改变默认为external链接属性的标识符声明:将其改变为internal属性,因此static修饰全局变量和函数都使其不能被跨文件访问;

extern指定一个标识符为external链接属性,但是extern只能用于标识符的第一次声明;

extern大部分时候(几乎)是被省略的,因为全局变量,函数都是external属性的,因此被省略也么什么影响,但是还是建议带上,可以使程序更加的清晰,extern必须用的时候就是下图中声明局部变量c将其指定为具有external属性,如果省略了那么它就变成了局部变量;

在这里插入图片描述

4.存储类型

4.1存储位置

变量的存储类型是指存储变量值的内存类型。变量的存储类型决定变量何时创建、何时销毁以及它的值将保持多久(生命周期);

存于内存:在内存中变量的存储可以在堆栈之中,可以在普通内存之中。

存于寄存器:关键字register用于局部变量的声明,提示将其储存在寄存器当中,但是编译器不一定会进行理会。尽管寄存器变量访问起来更加的高效,但是有可能将某些变量或者函数参数从堆栈复制到寄存器之中的消耗远远高于优化所节省的时间;

4.2变量的默认存储类型取决于它的声明位置

在任何代码块之外声明的变量总是存储于静态内存之中:也就是堆栈之外的内存,称之为静态变量。这类变量无法为它指定其他存储类型,在程序运行之前就已经创建了,在程序的整个执行期间一直存在,始终保持原来的值,除非赋予它一个不同的值或者程序结束;因此全局变量的生命周期为从声明开始直至程序结束

在代码块内部声明的变量默认存储类型是自动的:也就是说它存储在堆栈之中,称之为自动变量。有一个关键字auto,用于修饰这种变量,但是很少用,因为在代码块内部声明默认就是自动变量了。在程序执行到自动变量的代码块时自动变量才会被创建,代码块执行完毕,变量又会被自动销毁,再次执行代码快时,变量的存储位置又大概率与上次不一样了,就算一样内容也很有可能已经发生了改变,因为不能保证这块内存没有被用于其他用途;这就可以很好的解释为什么局部变量的生命周期是从声明开始,直至对应的代码块结束;

以上两点很好的解释了为什么局部变量可以和全局变量同名,并且优先使用局部变量,因为局部变量和全局变量的存储位置不同,一个在静态内存之中,一个在动态内存之中。 下图4-1所演示,两种存储位置的变量的生命周期(附内存空间分布图)

在这里插入图片描述

4.3 static和extern

static:对于局部变量的声明,给它加上static会修改它的存储位置,由堆栈变为静态存储区,因此生命周期由从声明开始至代码块结束变成了从声明开始直至程序结束。作用域不会发生改变,因为作用域由它声明的位置决定;

extern: extern用于局部变量的声明时,将其指定为external属性,而具有external链接属性的实体总是具有静态存储类型,即引用的不是局部变量而是全局变量;

在这里插入图片描述

5.变量的初始化

静态变量的初始化:当文件载入内存的时候就完成了初始化,不需要额外的操作和时间以及指令,如果不给初始化值,默认为0;

自动变量的初始化:对自动变量先声明再赋值或者声明的同时给予初始化的值没有区别,这是因为在程序链接的时候还无法判断局部变量的存储位置,即每次自动变量的创建的位置都可能不相同。因此自动变量没有默认的初始值,显示的初始化也只是在局部变量所处代码块的起始位置插入一条隐式的赋值语句,因此这两种方式效率上并没有区别;同时因为这条隐式赋值语句使得自动变量在程序执行到该代码块时都会重新初始化;

6小结static作用

1.当static作用与全局变量以及函数的时候,改变的是其链接属性,即将external改变为internal属性,因此不能对static修饰的全局变量和函数进行跨文件访问;

2.当static作用于局部变量时,改变的是局部变量的储存方式,由储存在堆栈改变为储存在静态内存区,因此局部变量的生命周期变为全局属性,但是作用域不会改变,因为作用域是由变量的声明位置所决定的;

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值