预处理指令
一、预处理指令
程序员所编写的C代码并不能被直接编译,而是需要一段程序预先翻译成标准的C代码,负责翻译的程序叫预处理器,翻译的过程叫预处理,被翻译的代码叫预处理指令,所有预处理指令都以 # 开头。
gcc -E file.c 把预处理的结果显示到屏幕上
gcc -E file.c -o file.i 把预处理的结构输出到文件中
二、头文件包含指令
#include //把头文件的内容插入到当文件中。
#include <> //从系统指定的路径下查找头文件,并插入到当前文件中。
#include "" //先从当前路径下查找头文件,
//如果没有再从系统指定的路径下查找头文件,
//然后再插入到当前文件中。
三、宏定义指令
定义宏常量
#define 宏名 字面值
注意:末尾不要加分号
它的作用就是用标识符来替代字面值数据,从而提高代码的可读性、可扩展性。
代码在预处理时所使用的宏会被替换成字面值。
定义宏函数
#define 宏名(a,b) (a+a+b+b)
不是真正的函数,而是带参数的宏,预处理时使用了带参宏的位置替换成宏名后面的语句,所提供的参数也会被替换到语句中的位置。
优点:
1、不是真正的函数调用,没参数传递过程,也没返回值,所以运行速度比函数要快。
2、不限制参数类型,任何类型都可以使用,代码的通用性强。
缺点:
1、没有类型检查,安全性代低。
2、过多使用会造成代码冗余,导致代码段增大,浪费内存。
3、可能会产生二义性。
定义宏函数要注意的问题
1、给每个参数加一个小括号,避免产生二义性。
2、使用小括号、大括号包含整个宏函数代码,进行保护。
3、宏函数的代码不换行,可以使用续行符 \。
4、宏函数的参数不要使用自变运算符。
用宏函数定义交换函数:
#define swap(a,b) {typeof(a) t=a;a=b;b=t;}
四、编译器预定义的宏
__LINE__ //当前代码的行号
__func__ //获取当前函数名
__FILE__ //当前文件的名字
__DATE__ //获取当前日期
__TIME__ //获取当前时间
_cplusplus //分辨C++文件编译器和C++编译器
删除宏:
#undef //宏名
条件判断:
#if, #ifdef, #ifndef, #else, #elif, #endif
头文件卫士:
#ifndef、#define、#endif 防止头文件被重复包含
代码注释:
// 单行注释,使用麻烦,早期的编译器不支持
/*
//注释代码,但不能嵌套使用
*/
#if 0|1
//注释大段代码
#endif
判断代码的编译、运行的环境
#ifdef _cplusplus
printf("C++编译器")
#else
printf("C编译器")
#endif
#if defined(__APPLE__)
puts("我用的是mac os");
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
puts("我用的是windows os");
#elif defined(__linux__)
puts("我用的是linux os");
#endif
用于调试程序的宏函数
#ifdef DEBUG
#define debug(fmt,...)\
{\
printf("File:%s Line:%u Func:%sInfo:",__FILE__,__LINE__,__func__);\
printf("\033[01;34m");\
printf(fmt,##__VA_ARGS__);\
printf("\033[00m\n");\
}
#else
#define debug(fmt,...) if(0)
#endif
其它预处理指令
#error
“字符串” 产生一条件编译错误信息(不会生成可执行文件),与#if系列指令配合使用才有意义。
#warning
“字符串” 产生一条件编译警告信息(正常生成可执行文件),要与#if系列指令配合使用才有意义。
#line line_number "filename"
设置代码的行号和文件名。
#pragma
可以让编译器执行某些事. 因为#pragma命令的执行很特殊,不同的编译器使用有所不同。
#pragma pack(n)
用于修改内存对齐、补齐最大字节数 n等于2的n次方才有意义,n<默认最大字节数。
#pragma GCC poison <标识符>
把某个标识符定义为毒药,禁止使用,一般用于禁用goto语句。