一 问题

之前在阅读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

就不会出错,因为这是声明语句,也可以看做定义语句,但是编译器编译时会看做定义了一次,其它地方再出现相同语句就会当做声明。

谢谢阅读,如果有写的不对的地方,请留言指正,万分感谢。

Logo

更多推荐