学c很多年来只是知道、那些律法般的规则,直到实战才知道其真正的含义。
问题1. 关于防止嵌套包含宏定义开关
在每个头文件的开头和结尾是头文件的宏定义开关:
#ifndef XXXX
#define XXXX
----
----
#endif
这是为了防止在一个源文件中重复引用同一个头文件造成的重引用。我可以理解在同一个问题中防止嵌套的功用,但是如果两个源文件同时引用同一个头文件呢,毕竟XXXX是一个开关,当第一次已经被定义后,第二个引用的源文件岂不是被开关挡在内容之外?但是我们平时使用并没有出现这种现象,那么原因何在?
看一个例子就知道了。
定义2个文件a.h a.c
//a.h
1 #ifndef __aaa__
2 #define __aaa__ 11
3 int a;
4 #endif
//a.c
1 #include "aa.h"
2 int main()
3 {
4 a=__aaa__;
5 return 0;
6 }
预编译一下
#gcc -E a.c -o a.i
打开a.i
1 # 1 "aa.c"
2 # 1 "<built-in>"
3 # 1 "<command-line>"
4 # 1 "/usr/include/stdc-predef.h" 1 3 4
5 # 1 "<command-line>" 2
6 # 1 "aa.c"
7 # 1 "aa.h" 1
8
9
10 int a;
11 # 2 "aa.c" 2
12 int main()
13 {
14 a=11;
15
16 return 0;
17 }
我们可以看到在预编译完成的文件中并没有__aaa__,在我们引用的地方已经被替换成了11,这样在后续的编译链接阶段,__aaa__是不存在的,也就不会有冲突的问题了,毕竟重定义的冲突要到链接阶段才会别发现。于是问题解决。
问题2. 关于定义变量的问题
应该是我没学好c,我一直以为要把全局变量定义或声明在头文件中。指定我在大型工程中出现重定义的错误。
原因是显然的,一个供超过2个源文件引用的头文件里面定义或声明的变量会被copy多份,这样在链接的时候就会出现多重定义的错误。解决办法是在某个.c文件中定义和声明全局变量而在头文件中加extern,这样头文件就可以被多源文件引用而不会报错了。
问题3. 关于声明函数的问题
在我没接触大型c工程时,认为在头文件中声明函数是不容置疑的正确。而且就是把在源文件中的声明照搬到头文件的那种。事实并非如此。
声明函数同样会遇到问题2中的情形。被引用的头文件会被copy多次,进而产生多重声明的错误。
解决办法是,如果一个头文件只会被某个源文件引用,那么可以将它直接放在头文件中,不用做修改,也不会出问题,这个同样适用于上一个问题。对于一般的情形,有一种办法是在声明函数前加static或static inline。这样就会使得函数只在引用的源文件中起作用,使其scope变成文件内可见而非全局的。加inline就是将函数直接展开,加不加要看情况。
最后,送给自己:纸上得来终觉浅,绝知此事要躬行。