【保姆级讲解】C语言---预编译处理精华
预编译处理
1.1预编译处理概述
预编译处理也称为编译预处理,是在程序正式编译之前需要进行的前期编译处理阶段。主要作用是向编译器传递信息。以井号(“#”)开头的命令都是编译预处理命令,并且编译预处理命令后面没有分号(“;”)。
预处理:头文件 #include <stdio.h> 预处理阶段进行的(完成替换) 后面没有;
预处理阶段相关命令:GNU套件中的gcc编译器 参数-E 生成文件后缀为xxx.i
未包含头文件的预处理与源文件的比较:
编译预处理命令格式:gcc -E 1.c -o 1.i
包含头文件的预处理与源文件的比较:
进行替换:
预处理==替换
1.2常用预编译处理命令
- 文件包含命令(#include)
#include预编译命令主要用于插入(包含)头文件。使用“#include <头文件名称>”或“#include “头文件名称””的格式来插入(包含)一个具体的头文件。尖括号(<>)和双引号(””)表示头文件的查找路径不同:
- <>:在系统指定目录(软件安装目录)下查找需要插入的头文件。
Linux中寻找的为/usr/include/文件夹;
- “”:在整个工程目录下查找插入的头文件,如果找不到,再到系统指定目录(软件安装目录)下查找。
情况1:
#include “stdio.h”
如果你的工程目录下有stdio.h则发生替换需要采用工程下stdio.h不再使用标准库下的stdio.h
如果您的工程目录下没有stdio.h则查找标准库下的头文件;
以上编译器首先查找当前工程目录下stdio.h;找到出现错误---没有printf函数;
情况2:
自己编写字符串函数头文件为string.h,即可抛出标准库string.h----头文件格式一定是””;
情况3:
当前目录下没有stdio.h文件,采用#include ”stdio.h”形式,归根到底还是查找标准库中的<stdio.h>;这样做的结果是:编译预处理时间耗时变长;(查找是需要时间的);
以后#include “”与<>要合理;
- 宏定义(#define)
- 宏定义概述
宏定义就是替换的意思,主要是把表达式、常量等内容,利用一个自定义的标识符号进行替换。
替换意思:位置不变 只替换内容
原:Abcdef:将cd替换为12 结果:Ab12ef
- 宏应用
- 无参宏
无参宏是指这个宏定义不需要参数的输入,仅仅是用于替换。使用“#define A B”的格式来进行无参宏定义。
- 替换一个数据类型 -- #define 别名 数据类型
#include <stdio.h> //a)替换一个数据类型 -- #define 别名 数据类型 #define DATA_INT int //宏定义为大写标识 int main() { DATA_INT A = 10; DATA_INT C =A+13; printf("%d\n",C); return 0; } |
gcc -E 1.c -o 1.i预处理阶段会将define宏定义的内容进行替换;
- 替换一个表达式
#define A 1+2 //表达式与语句区别 表达式是由运算符结合起来的式子;语句是表达式后面加;
经常犯错的情况:
#include <stdio.h> //a)替换一个数据类型 -- #define 别名 数据类型 #define DATA_INT int //宏定义为大写标识 #define NUM 1+2 int main() { DATA_INT A = 9; DATA_INT C =A/NUM;//9/1+2 9+2 printf("%d\n",C); return 0; } |
- 有参宏
有参宏是指在宏定义中的参数称为形参,在宏调用中的参数称为实参。对带参的宏,在调用的过程中,不仅要宏展开,而且还需要使用实参去代替形参。
- 替换一个运算公式
#include <stdio.h> #define MIN(x) x int main() { int A =10; printf("%d\n",MIN(A)); return 0; } |
例如:define M(y) y+y
#include <stdio.h> #define M(y) y+y int main() { int C = M(4)/M(2); //y+y/y+y 4+4/2+2 8 printf("%d\n",C); return 0; } |
替换函数
例如:#define LEN(x) strlen(x)
#include <stdio.h> #include <string.h> #define LEN(x) strlen(x) int main() { printf("%d\n",LEN("hello")); return 0; } |
宏只是进行替换的,变量是负责在内存中占用时的标识;
如果一个宏定义代替多条语句,建议采用do{}while(0)形式:
#include <stdio.h> #include <string.h> // #define PRINT do{printf("1111112222\n"); printf("22222233333\n");}while(0) int main() { PRINT; return 0; } |
1.3条件编译
条件编译是根据实际宏定义(某类条件)进行代码静态编译的手段。可以根据表达式的值或某种特定宏是否被定义来确定编译的条件。
- 条件与编译模型1
模型1和模型3一般常用于对操作系统、GUI程序等进行功能裁剪(如果表达式为真,则对这段程序进行编译或有xxx这个定义,则这段程序有效)。
#define 标识符 #ifdef 标识符 程序段1; #else 程序段2; #endif 运行规律:如果标识符已经被定义,执行程序段1,未定义,执行程序段2。 |
例:
#include <stdio.h> #include <string.h> #define _INIT_ //:如果标识符已经被定义,执行程序段1,未定义,执行程序段2。 int main() { #ifdef _INIT_ int A =10; int B =20; int C = A+B; printf("int C = A+B:%d\n",C); #else int A =20; int B =30; int C =B-A; printf("int C =B-A:%d\n",C); #endif return 0; } |
注意:
int _INIT_;
#define _INIT_
条件编译标识与变量名是不一致的;需要区别;
条件与编译模型2
模型2一般用于防止头文件的重复编译。
#ifndef 标识符 程序段1; #else 程序段2; #endif 运行规律:如果没有定义标识符,执行程序段1,如果已经定义,执行程序段2。 |
#include <stdio.h> #include <string.h> #define _INIT_ int main() { #ifndef _INIT_ //如果没有定义则运行下面表达式 如果定义则运行else int A =10; int B =20; int C = A+B; printf("int C = A+B:%d\n",C); #else int A =20; int B =30; int C =B-A; printf("int C =B-A:%d\n",C); #endif return 0; } |
#define与ifdef或者ifndef是有前后顺序要求的;
条件与编译模型3
#if 条件表达式 程序段1; #else 程序段2; #endif 运行规律:条件表达式为真,编译程序段1,如果为假,编译程序段2 |
#include <stdio.h> #include <string.h> #define _INIT_ #define NUMA 30 #define NUM 20 int main() { #if NUMA > NUMB //1 //0 int A =10; int B =20; int C = A+B; printf("int C = A+B:%d\n",C); #else int A =20; int B =30; int C =B-A; printf("int C =B-A:%d\n",C); #endif return 0; } |
注:在所有条件预编译中#else可以没有,但#endif(结束条件编译必须存在)。模型1和模型3一般常用于对操作系统、GUI程序等进行功能裁剪(如果表达式为真,在编译这段程序或有xxx这个定义,则这段程序有效)。模型2一般用于防止头文件的重复编译。