#include 的花样

来源:互联网 时间:1970-01-01

用过C的都知道#include,写hello world之前不都得#include <stdio.h>么。不过#include这种东西,会有很多花样,有些是常用的正规用法,有些就是很evil的用法了。

1,条件包含

例如,由于vc没有提供C99的stdint.h,所以跨平台的代码可能会有这样的语句:

#ifdef _MSC_VER typedef __int64 int64_t#else #include <stdint.h>#endif

这是常见的正规做法。

2,包含非头文件

譬如有些.c文件里面会写,#include "inc/aes_impl.c",这种做法编译器不管(当然也管不着,这是cpp干的事儿)。通常这出现在手懒的程序员的身上,他们懒得再写一个头文件,然后用正规的做法了。这种做法通常是比较evil的行为:被包含的部分如果没有将作用域限制到文件内,则在其他地方再用的话,会有重复定义;而如果限定了,其他地方再用虽然没有重复定义了,但最终的binary里面有多个拷贝,不好。

但事情得分开看,放到一起的话,免了头文件的解析,也免了obj文件生成和链接,可以加快编译的速度。对于大型项目,编译时间是一个比较大的问题,加快编译速度的方法之一,就是将所有的源文件搞到一个文件里面,然后只编译这一个文件,这样可以显著加快编译速度,即所谓unity build。

3,诡异的include位置

通常include都是在文件头,不过也有诡异的include位置,譬如某SDK的代码中,某class的定义如下:

class HPMSdkSession{ private: ... public: ... public: #include "HPMSdkCpp_AutoFunctions.h"};

这里,根据名字大概可以猜得到,这些auto functions,可能是自动生成的代码,或者相对独立的代码。这种做法。。也还行吧,虽然不那么规整。

4,其他诡异的include

其一,下面的代码,可以用来检测所用的编译器支持多少层的include

#include __FILE__

因为有时候会有循环include的情况,譬如a.h引用了b.h,b.h引用了c.h,c.h又引用了a.h。这种情况下,cpp会陷入死循环。为了避免这种情况,编译器会对include的层次有个限制。

其二,一种很evil的做法,

 

#include "/dev/stdin"

当然,编译的时候,得从stdin给点什么,否则编译不下去了。可以试一试.c文件里面只放这一句,然后从终端输入源码,^D结束以后得到(譬如)a.out,还是挺好玩的。

再比如,要在可执行文件里面加上一个编译的时间戳,可以这样,源文件里面这样写:

#include "dev/stdin"#include <stdio.h>int main(int argc, char *argv[]){ printf("COMPILE_TIME: %s/n", COMPILE_TIME); return 0;}

编译的时候:echo /#define COMPILE_TIME /"`date`/" | gcc -Wall hello.c -o hello

不过这种做法实在是evil,笑笑就算了,正规的做法还是makefile里面定义一下,然后gcc -D。

相关阅读:
Top