静态全局变量和静态局部变量的探索
一、静态全局变量
1.特点
静态全局变量具有文件作用域,生命期是所处模块装载到所处模块卸载,处于同一文件中的代码能直接访问它,外部文件不能直接访问。假设写了如下代码,编译通过。
现在在同一工程下新建文件,输入如下代码,编译通过,链接失败。
如果换成全局变量,则编译连接成功:
由此可见,在用extern定义变量时,只能在本文件使用,其他文件无法直接访问。
2.机制
静态全局变量本质上是受编译器按语法约束的全局变量,如果未初始化,则分配在数据区的未初始化去,如果已初始化,则分配在数据区的已初始化区域。
可以观察到已初始化的静态全局变量和全局变量地址紧邻,未初始化的静态全局变量和全局变量地址紧邻。已初始化的全局变量和未初始化的全局变量之间有一定差距。
3.适用场合
某些数据或函数不想被外部文件访问时,可定义为静态全局变量。好处是提供了一定程度的封装,高内聚、低耦合、分责任。
二、静态局部变量
1.特点
静态局部变量只能在定义所在作用域内访问,具有块作用域,生命期是所处模块装载到所处模块卸载。
假设写了如下代码,编译无法通过,报错“error C2065: “nTest1”: 未声明的标识符”:
2.机制
2.1初始化为常量时
写如下代码,可以发现静态全局变量,静态局部变量,全局变量的地址紧邻。
2.2初始为变量时
C文件中,编译不通过:
cpp文件可编译通过(使用VC6):
第一次给nTest1赋值时,00427E50处变为1
单步一次,变为00427E50处变为3
再单步一次,变为07
推测每一个bit保存局部静态变量是否初始化,为0则表示未初始化,1表示已初始化,所以运行时访问此bit时,为1则跳过初始赋值代码。
照此推测,修改为0,应该会再次赋值:
单步,发现确实修改了
继续单步
继续单步
照此推测,将00427E50处改为5,则00427E58处会被赋值,其余两处不会。先手动修改:
单步一次:
单步两次:
单步三次:
符合推测,所以vc6编译器用一个bit保存局部静态变量是否初始化,为0则表示未初始化,1表示已初始化。
2.3 编译器如何控制它跨界访问
c文件中定义static_ABC的局部静态变量,更改调用约定,参数,返回值,每次改一个。再去main.obj里搜索static_ABC,发现均无变化,在static_ABC周围加上花括号,则有变化。
?static_ABC@?3??Foo1@@9@9 2层花括号,函数名Foo1
?static_ABC@?2??Foo1@@9@9 1层花括号
?static_ABC@?1??Foo1@@9@9 无额外层花括号
?static_ABC@?3??Foo2@@9@9 2层花括号,函数名2
?static_ABC@?2??Foo2@@9@9 1层花括号
?static_ABC@?1??Foo2@@9@9 无额外层花括号
在cpp文件中则和返回类型,参数类型,参数个数,调用约定均有关联。表现如下:
//?static_ABC@?1??Foo1@@YAXXZ@4HA
//?static_ABC@?2??Foo1@@YAXXZ@4HA
//?static_ABC@?3??Foo1@@YAXXZ@4HA
//?static_ABC@?3??Foo1@@YAXH@Z@4HA
//?static_ABC@?3??Foo1@@YAXHH@Z@4HA
//?static_ABC@?3??Foo1@@YAXHHH@Z@4HA
//?static_ABC@?3??Foo1@@YAHHHH@Z@4HA
//?static_ABC@?3??Foo1@@YANHHH@Z@4HA
//?static_ABC@?3??Foo1@@YGNHHH@Z@4HA
//?static_ABC@?3??Foo1@@YINHHH@Z@4HA
//?static_ABC@?3??Foo1@@YANHHHZZ@4HA