预定义符号
- _FILE_:进行编译的源文件
- _LINE_:文件当前的行号
- _DATE_:文件被编译的日期
- _TIME_:文件被编译的时间
- _STDC_:如果编译器遵循ANSI C,其值为1,否则未定义
宏定义
宏替换
- 调用宏时,检查参数知否包含#define定义的符号,如果是,首先替换,字符串常量的内容不被检查
- 随后替换后的文本插入程序原来文本的位置,对于宏,参数名被它们的值替换
- 最后对结果文件进行扫描,检查是否包含#define定义的符号,如果有,就重复上述过程
注意:宏中不能出现宏递归
#和##
- #:把一个宏参数变为对应的字符串
- ##:将位于##两边的符号合为一个符号
宏的优点
宏的缺点
- 多处调用宏,可能会造成代码膨胀
- 宏的优先级需格外注意
- 带有副作用的宏参数进行多次求值,可能会得到意料之外的结果(++,--)
- 宏与类型无关,也就不够严谨
- 由于宏在预处理阶段进行宏替换,所以无法调试
宏和函数
- 宏不可以调试,函数可以
- 代码长度:宏每次使用都会使宏代码被替换到代码中,可能会造成代码膨胀;函数代码只出现在一个地方,每次使用函数,都会跳到同一个地方
- 执行速度:宏在预处理阶段完成了宏替换,速度更快;函数需要创建栈帧,压参传参,返回值的开销
- 操作符优先级:宏由于直接替换容易造成意料之外的结果;函数的求值结果更容易预测
- 参数求值:宏在每次调用都会重新求值,而且有副作用的参数可能会产生不可预料的结果;函数只在调用前计算一次,不会出现副作用
- 参数类型:宏与类型无关,只要对参数的操作合法,可以使用任何参数类型;函数与参数类型有关,参数类型不同执行不同的代码
宏和枚举
- 宏不能调试,枚举可以
- 宏在预处理阶段进行简单的替换,而枚举在编译阶段确定值
- 枚举一次性可以定义大量的常量,而宏只可定义一个
宏和内联函数
- 宏不能调试,内联可以
- 宏对参数不进行类型检查,内联函数进行类型检查
- 宏肯定被替换,内联只是一种建议
- 宏不注意优先级会出现意料之外结果,内联一般不会
typedef和define
- define可以使用类型修饰符,而typedef不能
- typedef定义的类型可保证所有变量为同一类型,define无法保证
头文件展开
- #include <filename>:到头文件标准路径下查找
- #include "filename":先在源文件所在目录下查找,如果未查到,编译器到头文件标准路径下查找
条件编译
#if 常量表达式
//...
#endif
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//..
#endif
#if defined(symbol)
#ifedf symbol
#if defined(symbol)
#ifnedf symbol
#if defined(os_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
nuix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif
#ifndef __TEST_H__ //如果不存在test.h
#define __TEST_H__ //引入test.h
//头文件的内容
#endif //否则不需要引入
- #program once:保证头文件只被包含一次
- #program pack():设置默认对齐数,用于结构体内存对齐