预处理指令
预处理指令
程序员所编写的c代码并不能直接被编译,而是需要一段程序预先翻译成标准的c代码,负责处理的程序就叫预处理器,翻译的过程就叫做预处理,被翻译的代码就叫做预处理指令,所有预处理指令都是以#开头
gcc -E file.c 把预处理结果显示在屏幕上
gcc E file.c -o file.i 把预处理结果输出到文件上
include 把头文件插入当前文件中
#include<> 从系统指定的路径下查找头文件,并插入当前文件中。
#include"" 先从当前路径下查找头文件,如果没有再从系统指定路径下查找头文件,然后再插入当前文件中。
define 宏定义指令
定义号宏常量
#define 宏名 (字面值)
注意:末尾不要加分号
作用:用标识符来替代字面值数据,从而提高代码的可读性,可扩展性。
代码在预处理时所使用的宏名会被替换成字面值。
#define 宏名(a,b,c) (a+b+c)
不是真正的函数,而是在使用了带参数的宏,预处理时使用了带参宏的位置替换成宏名后面的语句,所提供的的参数也会被替换到语句中的位置。
优点:
- 不是真正的函数调用,没有参数传递过程,也没有返回值
- 不限制参数类型,任何类型都可以使用,代码的通用性强
缺点:
- 没有类型检查,安全性低
- 过多使用会造成代码冗SW余,导致代码段增大,浪费内存。
- 可能会有二义性(多加点括号)
定义宏函数要注意的问题:
- 给每个参数加一个小括号,避免产生二义性
- 使用小括号、大括号包含整个宏函数代码,进行保护
- 宏函数不能换行可以使用续行符 \ 来分行一下,但是还是一行
- 宏函数参数不要使用自变运算符。
练习
定义一个交换两个变量的宏函数,要任何类型的变量都可以使用
the first way
#define swap(a,b,type) { \
type t=a; \
a=b; \
b=t; \
}
the second way
#define swap(a,b) {typeof(a) t=a; a=b; b=t;}
在编译时也可以定义宏:
gcc -D DEBUG
预定义好的宏:
__func__ 获取函数名
__FILE__ 获取文件名
__LINE__ 获取当前行号
__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
用于调试程序的DEBUG函数:
#ifdef DEBUG
#define debug(fmt,...)\
{\
printf("File:%s Line:%u Func:%s Info:",__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语句。