概念
- 预处理命令是以
#
开头的命令,在编译启动时,首先会执行预处理程序来完成预处理,再进行编译和链接 - 预处理命令对源文件(文本文件)进行简单的文本替换:将代码当场普通文本,进行头文件导入、宏定义、条件编译…
- 一般位于源文件最前面
#include
将头文件(.h)内容插入到源文件中#include命令所在位置
#include <header.h> // 到系统路径下查找头文件
#include "header,h" // 先在当前目录下查找头文件,再到系统路径下查找
""
的功能比<>
更强大,标准头文件在系统路径下,用<>
就足够了,自己编写的头文件一般放在当前目录,需要用""
- 用法举例:
c语言一般将需要调用的函数和main()
放在不同源文件中,将函数声明放在头文件,并用#include
将声明导入main()
所在源文件
my.c代码:
my.h代码:int sum(int a, int b){ return a+b; }
main.c代码:int sum(int, int)
#include <stdio.h> #include "my.h" int main() { printf("%d", sum(1,2)); }
宏定义
宏定义就是用一个标识符(宏名)表示一个字符串(即源文件中的代码文本)
#define 宏名 字符串 // 预处理时,代码文本中的宏名会被替换成字符串
- 宏定义中的字符串是一般意义里的字符序列,不是c语言中的字符串,无需加
""
- 用字符串替换宏名,是简单粗暴的文本替换,字符串中可以含任何字符,预处理程序对它不作任何检查,如有错误就只能在编译已被宏展开后的源程序时发现
- 字符串表达式一般要加
()
!防止替换后因为运算优先级出问题 - 宏定义不是语句,行末无需加
;
,如加上则分号也会加入替换 - 宏定义必须在函数外,其作用域是从宏定义命令起,到源程序结束;如要提前终止作用域,可以用
#undef
命令 - 代码中的宏名如果被引号包围,预处理程序就不会对它宏替换
#define OK 100 void main() { printf("OK\n"); \\ OK }
- 宏名允许嵌套
#define PI 3.141596 #define C 2*PI*r
- 可以用宏名表示数据类型,但这和typedef定义数据类型说明符存在区别
宏名是简单的文本替换,不涉及编程语法;而typedef 数据类型 新的数据类型名;
是在编译时进行的,是在语法层面上给原有的数据类型起了个新名字#define P1 int * typedef int(*P2);
宏名替换后为P1 a,b;
int *a,b;
,a为int *类型,b为int类型
P2在typedef后语法上指的就是int *类型,所以,a和b均为int *类型P2 a,b;
- 预定义宏:c语言已经预先定义好的宏,可以直接使用
printf("Date : %s\n", __DATE__); // 当前编译日期 printf("Time : %s\n", __TIME__); // 当前编译时间 printf("File : %s\n", __FILE__); // 当前源文件名 printf("Line : %d\n", __LINE__); //当前行号
带参宏定义
对带参数的宏,在展开过程中,不仅要用字符串替换宏名,还要用实参替换形参
定义:
#define 宏名(形参列表) 字符串
调用:
宏名(实参列表);
例如:
#define SQ(y) ((y)*(y)) // 定义
k = SQ(5) // 调用:展开后为k=((5)*(5))
-
带参宏定义时,宏名和形参列表之间不能存在空格,否则会被识别为不带参的宏定义
#define SQ (y) ((y)*(y)) // SQ会被直接替换成(y) ((y)*(y))
-
形参列表无需指明数据类型:因为带参宏定义只是简单的文本替换,将形参文本替换成实参文本,并不会真的为形参分配内存;而函数是将实参的值copy给形参
#define SQ1(y) ((y)*(y)) int SQ2(int y) { return y * y; } void main() { int a =1, b = 1; printf("%d, %d\n", a, SQ1(a++)); //输出3,1:展开为((a++)*(a++)),两次自加 printf("%d, %d", b, SQ2(b++)); // 输出2, 1:实参传值给形参,只进行一次自加 }
-
字符串内的形参和整个字符串通常要用括号括起来以避免出错!
#define SQ(y) y*y // 如果字符串内形参不加括号,字符串也不加括号
k = SQ(a+1) +1 // 展开为a+1*a+1+1
条件编译
编译源程序中满足条件的程序段,使生成的目标程序较短,减少内存的开销并提高程序的效率
1) #if 条件为整型常量表达式
如果条件值为真即整型常量表达式值非0,就编译对应代码块
#if 整型常量表达式
代码块
#elif 整型常量表达式
代码块
...
#elif 整型常量表达式
代码块
#else
代码块
#endif
- 表达式中不能含变量,且结果必须为整数
- #elif和#else可省略,可以是单分支,双分支,多分支
2)#ifdef和#ifndef 条件为宏名
#ifdef 如果宏名已被定义,就编译对应代码块
#ifndef 如果宏名还没有被定义,就编译对应代码块
#ifdef 宏名
代码块
#else
代码块
#endif
#else可省略,可以是单分支,双分支
#error
预处理到#error
命令时将停止编译并输出用户自定义的错误消息
#error 自定义错误消息