C语言头文件到底是什么?
在C语言学习的时候总是会引入这样的语句#include <stdio.h>,书上解释说把stdio.h这个文件的全部内容直接插入到这个位置,然后再经过C语言的编译器编译运行。这么看来隐含的意思好像是.h头文件好想并不直接参与编译。
围绕这个话题引出了下面这几个问题。
一,.h头文件会参与编译吗?不妨来做个实验
这个是head.h文件的内容
#include <stdio.h> int main() { printf("Hello World!"); return 0; }这个是ori.c文件的内容
#include "head.h"编译执行gcc ori.c -o ori
发现输出的是
>> .\ori.exe >> hello world!.c文件中并没有引入任何其他的文件,除了我们自己定义的head.h头文件,而在这个头文件中,我们引入了stdio.h头文件,并且我们在head.h里面定义的main函数被执行了,由此证明了include xxx.h是直接原封不同的插入到引用这个头文件的.c文件中的。
但是会参与编译吗?
为此设计下面这个实验:
开一个项目,在.h头文件里面定义main函数,
不引用这个头文件直接编译整个项目,然后执行,
如果没有输出main函数内部的内容,那么表明如果不加引用,.h文件不参与编译。
否则就参与编译。
这个是head.h文件内容
#include <stdio.h> int main() { printf("Hello World! I am head.h"); return 0; }这个是ori.c文件的内容
// nothing编译执行gcc ori.c -o ori
发现输出的是
C:/Program Files/MinGW/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64 _libmingw32_a-crt0_c.o):crt0_c.c:(.text.startup+0x2e): undefined reference to `WinMain' collect2.exe: error: ld returned 1 exit status报错了,undefined reference to WinMain未定义WinMain
这时候我们在ori.c内部定义这样的一个函数
#include <stdio.h> int WinMain() { printf("Hello world! I am WinMain!"); return 0; }再次编译运行gcc ori.c -o ori
运行.\ori.exe
>> .\ori.exe >> Hello world! I am WinMain!发现正常输出了WinMain函数内部的内容,这也告诉我们WinMain函数同样可以作为程序的入口。
经过实验发现,的的确确.h绝对不是直接参与编译的,而是通过.c文件中#include "xxx.h"语句手动插入的。
这其实间接解答了在.h头文件中定义的静态局部变量无法局部化的原因。
二, .h头文件中的静态全局变量为什么可以被访问?我们在学习C语言初期就直到,如果对一个全局变量使用static语句修饰的话,就可以把这个变量限制在本文件的访问域内,而无法被其他文件访问,但是这一点对于.h文件中无效,该访问还是可以访问?下面看一下实验
创建三个文件ori1.c, ori2.c, head.h
在ori1.c中写下以下内容
#include <stdio.h> extern int A; extern int B; int main() { printf("A = %d\n", A); printf("B = %d", B); return 0; }在ori2.c中写下以下内容
#include <stdio.h> int A = 12; static B = 13;此时在head.h中不写入任何内容
编译运行
正如和我们学过的一样,出现了未定义的错误
undefined reference to `B' collect2.exe: error: ld returned 1 exit status静态全局变量不可以被其他变量修改
这时候我们给ori2.h添加两个函数
void getB() { printf("B = %d\n", B); } void changeB(int num) { B = num; }然后在ori1.c中去声明应用一下这两个函数
#include <stdio.h> extern int A; extern void getB(); extern void changeB(int B); int main() { printf("A = %d\n", A); getB(); changeB(1600); getB(); return 0; }编译运行
>> A = 12 B = 13 B = 1600虽然无法直接访问,但是可以定义ori2.c文件里面的函数去对该变量访问,这或许就是私有变量的雏形吧!
下面再来看看有关于.h中的全局变量,
由于我们已经知道了是完全插入的形式,那么我们可以理解到这个定义的静态全局变量是定义在引用它的文件里面
根据前文中定义在自己文件内部的静态全局变量只能被自己访问,那么结果自然显而易见了,
.h文件中的静态全局变量作用域是引用这个头文件的.c文件
自然而然的,我们应当明白,如果一个.h文件中定义过多的全局变量,那么这个全局变量被其他的变量引用的时候就会出现访问无指向的错误!重复定义
建议尽可能少在.h头文件中定义全局变量,或者静态全局变量
三,条件编译C语言编译是把整个项目编译,而很多的.c文件都需要引用同一个函数,而正对于不同的系统又出现了不同的编译模式,如果说要把所有的.c文件头部都写入那些描述条件编译的代码,那么所有的代码写起来会复杂无比
所以把条件编译代码放在.h文件里面,直接配置好才是一个好的选择。