定义:
预处理器在源代码进行编译之前对其进行一些文本性质的操作;
提供以下三种预处理功能:
1. 宏定义 (#define)
2. 文件包含 (#include
3. 条件编译 (#if #else #endif #ifndef #ifdef)
注:这些预处理命令是由ANSI C统一规定的,它不是C语言本身的组成部分,不能对其直接进行编译。
作用:
它的任务主要包括删除注释,插入被 #include 指令包含的文件内容,定义和替换由 #define 指令定义的符号以及确定代码的部分内容是否应该根据一些条件编译指令进行编译。
1.无参数的宏
*宏名一般习惯用大写字母表示,以便与变量名相区别,但这并非C标准规定;
*使用宏名代替一个字符串,可以减少程序中重复书写字符串的工作量;
*宏定义是用宏名代替一个字符串,只作简单的置换,不作正确性检查;
*宏定义不是C语句,不必在行未加“;”号,如果加了连“;”号一起进行置换;
*对带参数的宏展开只是将语句中的宏名后面括号内的实参字符串代替#define命令中的形参;
*在宏定义时,在宏名与带参数的括号之间不应有空格,否则将空格以后的字符都作为替代字符串的一部分;
*宏名的有效范围为定义命令之后到本源文件结束,可以用#undef NAME终止宏定义的作用域;
*函数调用时,先求出实参表达式的值再代入形参,而带参宏只进行简单的字符替换;
*函数调用是在程序运行时处理的,为形参分配内存空间。而宏展开是在编译前进行的,在展开时并不分配内存空间,不进行值传递,也没有返回值;
*对函数中的实参和形参都要定义类型,并且要求一致;而宏不存在类型问题;
*使用宏会使源程序变长,而函数调用不会;
*宏替换不占运行时间,只占编译时间。而函数调用则占用运行时间(分配单元,保留现场,值传递,返回)
*利用邻近字符串自动连接的特性:
*利用预处理器把一个参数转换成为一个字符串,如 #argu 会被 译为 “argu”
#if 常量条件
#endif //
#if 常量条件
#else
#endif
#if 常量条件
#elif 常量条件
#elif 常量条件
#else
#endif
判断某个符号是否被定义
#ifdef NR 等价的写法 #if defined NR
#else
#endif
#ifndef NR 等价的写法 #if !defined NR
#else
#endif
将文件内容原样插入到include处 (支持嵌套)
#include
#include “stdio.h” // 在当前路径找 如果没有则找标准路径
#include “/usr/include/stdio.h”
<>: 在标准路径下查找被包含的文件
“”: 首先在指定的路径找 如果没找到则找标准路径
gcc -I path xxx.c: 指定头文件的搜索路径
预处理命令总共有三种: 宏定义, 条件编译, 头文件包含;
宏定义: #define, #undef
① #define 只管一行,如果有多行,需要使用换行符’’,对于多行的宏定义,应该使用代码块符号"{}"括起来成为一个代码块.
② 宏模拟函数时,由于宏无法像函数那样实现返回值,所以可以使用小括号符号"()",把代码块括起来,这样就通过类似于逗号表达式功能模拟函数返回值,其返回值为最后一条表达式.
③ # 和 ##
‘#’: 字符串转换符, #a 把参数a宏替换后加上 “” 将其转变为字符串,然后利用相邻字符串会自动粘贴为一个字符串(中间只能为空白符)的特性将其合并到字符串中, 即: #a 变成 “a”, 而"a"“b” == “ab”.("a"和"b"之间只能插入空白符或没有符号)
‘##’: 粘贴符, 将宏参数替换出来后与前后字符合并为一体,如: i = 1; 而 ar##i 等价于ar1.
④ #unddef 控制宏定义作用域.
⑤ 命令行开启宏定义: gcc main.c -D(Define) DEBUG 这样编译的时候就定义了宏 DEBUG, 其默认值为1, 当然可以指定值: DEBUG=255
条件编译: #if-#else-#endif, #ifndef-#define-#endif, #ifdef-#endif
#if 后只接真假值(0,1), 为1则编译 #if 下面的程序一直到 #else(如果有的话)或者到 #endif
#ifdef(#if define) 后接宏定义符号,如果宏定义了该符号则编译下面的程序,否则不编译.
#error指令 允许生成错误信息
#ifdef option
expression
#else
#error No option
#endif
设置4字节对齐:
# pragma pack(4)
取消字节对齐:
# pragma pack(1)
恢复默认字节对齐:
# pragma pack()