第一次去工作时,总是会对公司C项目的各种写法感到奇怪,虽然学过C语言但从来没见过这些写法,其中最奇怪的就是头文件开头的那些宏定义了,感觉像是做了啥,但又什么都没做。本文将从原理上解析几种常见的写法。
避免重复引用
#ifndef MY_APP_H
#define MY_APP_H
//用户代码
#endif
这一种是比较常见的,用于避免源文件重复引用头文件。当某源文件引用该头文件后,编译器就会在该源文件中生成对应的头文件关键字,并解析头文件的代码(比如进行宏替换、变量、函数引用等等)。如果该源文件引用的其它头文件里也引用该头文件,由于该头文件关键字已经生成,就不会重复解析这些代码了,避免了重复的定义声明。如下图:中间的两条引用逻辑上是不应该出现的,但项目工程那么大,谁知道会不会偶然写错呢?
在这里还有个误区,即不同的源文件引用同一个头文件是合法的,这种引用可以不需要上面的写法。因为源文件之间在编译阶段是不关联的,每个源文件都可以放心引用重复的头文件进行解析。只有在符号链接阶段才对各自函数和变量进行串联,到这一层早已和头文件无关了。
C++代码引入
#ifdef __cplusplus
extern "C" {
#endif
//C++代码
#ifdef __cplusplus
}
#endif
第二种是这样的。这其实是在C语言中编写C++代码的通道,可以将C++相关的代码和库放在这里面,编译器会根据__cplusplus关键字自动根据C++语法解析里面的C++内容(可能有些编译器关键字还不太一样,但基本是这个)。另外,C++语法是默认包含C的,所以你把C代码全部写在这里面也没有问题。
在头文件定义变量
#ifdef OS_GLOBALS
#define OS_EXT
#else
#define OS_EXT extern
#endif
这一种写法对于第一次遇到的同学来说是最奇怪的,该写法本质上也是为了避免重复定义,但与第一种写法又不太一样。通常来说,我们仅在源文件中定义全局变量,然后在某个头文件中用extern继承该变量,其它源文件要使用该全局变量就得引用该头文件。但如果我想让源文件足够整洁,只在头文件中定义全局变量(注意C语言虽然不建议该写法,但编译器并不禁止),就可以用这种写法,让不同的源文件在引用该头文件时具有不同的效果。如下图:
在源文件my_App.c中定义了宏OS_Globals,于是在头文件my_App.h中,编译器认为该段宏代码有效:
#define OS_EXT
于是“OS_EXT char myChar”就被替换成了“char myChar”,即在头文件中定义该全局变量。
而另一个源文件your_App.c中没有定义该宏,于是在头文件my_App.h中,编译器认为下面这段宏代码有效:
#define OS_EXT extern
于是“OS_EXT char myChar”就被替换成了“extern char myChar”,即在头文件中继承该全局变量,避免了重复的全局定义。