头文件中的静态变量的作用范围
一 问题之前在阅读linux内核代码时,发现一个奇怪的现象:一个头文件里定义了一个static变量,然后在另外一个.c文件里使用#include引入这个头文件,结果这个.c文件里就可以直接使用这个static变量了。好像跟我们学的static用法不一样,一般static声明的变量,其作用范围就是限制在定义该变量的源文件里。二 解答于是网上搜了下,在stackoverflow里发现有人问过...
一 问题
之前在阅读linux内核代码时,发现一个奇怪的现象:一个头文件里定义了一个static变量,然后在另外一个.c文件里使用#include引入这个头文件,结果这个.c文件里就可以直接使用这个static变量了。
好像跟我们学的static用法不一样,一般static声明的变量,其作用范围就是限制在定义该变量的源文件里。
二 解答
于是网上搜了下,在stackoverflow里发现有人问过这个问题了,这里就直接粘贴一下答案,
Static variables are local to the compilation unit. A compilation unit is basically a .cpp file with the contents of the .h file inserted in place of each #include directive.
意思是static变量对编译单元来说是local的,而一个编译单元是一个.cpp或.c文件,并包含使用#include引入的头文件的内容。
可以这样理解:一个.cpp或.c文件里包含了一个头文件,这个头文件里定义了static变量,在编译时会把头文件展开,变相等价于这个static变量是在这个.cpp或.c文件里定义的,这样原本头文件里定义的static变量的作用范围就扩大到整个.cpp或.c文件了。
于是在.cpp或.c文件里就可以调用头文件里定义的静态变量了。
三 延伸
下面贴一张《CSAPP》里的一张经典图,关于源码编译成可执行文件的,
头文件展开是发生在标黄的那一步,即预处理阶段,下面是书中关于预处理的解释,
Preprocessing phase.The preprocessor (cpp) modifies the original C program
according to directives that begin with the # character. For example, the
#include <stdio.h> command in line 1 of hello.c tells the preprocessor
to read the contents of the system header file stdio.h and insert it directly
into the program text. The result is another C program, typically with the .i
suffix.
四 代码验证
这里再用代码验证一下,并讲解一下注意要点,假设有4个源文件,如下,
common.h如下,定义并声明一个局部静态变量testData,
#ifndef _COMMON_H_
#define _COMMON_H_
static int testData = 10;
#endif
func.c内容如下,
#include "func.h"
#include "common.h"
int func(void)
{
return testData + 100;
}
func.h内容如下,
#ifndef _FUNC_H_
#define _FUNC_H_
int func(void);
#endif
main.c内容如下,
#include <stdio.h>
#include "func.h"
#include "common.h"
int main(void)
{
testData = 50;
printf("func: %d\n", func());
return 0;
}
使用下面命令进行编译连接,
gcc main.c func.c -o main
编译ok后运行,
可以看到main.c里虽然给testData赋值为50,但是函数func()中使用的testData的值还是原始值10
下面使用gcc对func.c进行预编译,
gcc -E func.c -o func.i
func.i的部分内容如下,
可以看到common.h在func.i文件中展开了,这样func.c就有了局部静态变量testData
下面再对main.c进行预编译,
gcc -E main.c -o main.i
main.i的部分内容如下,
可以看到main.c里同样展开了common.h,并且也拥有了局部静态变量testData
这里要注意的就是func.c里的testData和main.c里的是互相独立的,不会互相影响。
如果把common.h中定义testData的static修饰词去掉,再编译就会报错,提示多次重复定义,这是因为变量只能定义一次,common.h在func.c和main.c里都展开,就相当于定义了2次,就会出错;如果把赋值语句去掉,变成下面这种形式,
#ifndef _COMMON_H_
#define _COMMON_H_
int testData;
#endif
就不会出错,因为这是声明语句,也可以看做定义语句,但是编译器编译时会看做定义了一次,其它地方再出现相同语句就会当做声明。
谢谢阅读,如果有写的不对的地方,请留言指正,万分感谢。
更多推荐
所有评论(0)