0x01隐藏
当我们同时编译多个文件时,所有未加 static 前缀的全局变量和函数都具有全局可见性。为理解这句话,我举例来说明。我们要同时编译两个源文件,一个是 a.c,另一个是 main.c。
下面是 a.c 的内容:
char a = 'A'; // global variable void msg() { printf("Hello "); }
下面是 main.c 的内容:
int main(void) { extern char a; // extern variable must be declared before use printf("%c ", a); (void)msg(); return 0; }
程序的运行结果是:
A Hello
如果加了 static,就会对其它源文件隐藏。例如在 a 和 msg 的定义前加上 static,main.c 就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。static 可以用作函数和变量的前缀,对于函数来讲,static 的作用仅限于隐藏。
有的公司编码规范明确规定只用于本文件的函数要全部使用static关键字声明,这是一个良好的编码风格。
0x02持久
存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和 static 变量,只不过和全局变量比起来,static 可以控制变量的可见范围,说到底 static 还是用来隐藏的。虽然这种用法不常见例如下面这段代码
#include <stdio.h> void fun(int n) { static int nTest = n; printf("%d ", nTest); printf("%p ", &nTest); } void main() { fun(1); fun(2); fun(3); fun(4); }
运行结果
1 0085B14C 1 0085B14C 1 0085B14C 1 0085B14C
没有改变,值得注意的是在vs调试运行时直接查看反汇编
在vc++ 上这里查看的反汇编是空 其实可以理解为在赋值一次后就不做操作了 。
下个断点看看他的内存改变
可以看到在下一步时 第一个int字节 位置 被写入了01(int 4个字节) 下一个是他的初始标识被同时改为1
再次段步后我直接把内存下标01改成00
在执行就是
执行结果
1 0085B14C 2 0085B14C 2 0085B14C 2 0085B14C
也就是说他通过标识来判断是否是赋过值
那我们来执行下面这段代码
#include <stdio.h> void fun(int n) { static int nTest = n; printf("%d ", nTest); printf("%p ", &nTest); (&nTest)[1] = 0; (&nTest)[2] = 0; } void main() { fun(1); fun(2); fun(3); fun(4); }
把这个静态变量的后面2个int 字节赋0 他的运行结果就是
1 005AB14C 2 005AB14C 3 005AB14C 4 005AB14C
以上环境在vs2017上 13和vc6 都只用改 当然有的可能下标距离很远
(&nTest)[1] = 0;
0x03初始化
其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是 0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置 0,然后把不是 0 的几个元素赋值。如果定义成静态的,就省去了一开始置 0 的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加