C语言__预处理(宏定义、文件包含、条件编译)
预处理简单理解
1.C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译
2.为了区分预处理指令和一般的C语句,所有预处理指令都以符号"#"开头,并且结尾不用分号
3.预处理指令可以出现在程序的任何位置,它的作用范围是从它出现的位置到文件尾。习惯上我们尽可能将预处理指令写在源程序开头,这种情况下,它的作用范围就是整个源程序文件
4.C语言提供的预处理指令主要有:宏定义、文件包含、条件编译
宏定义分类——不带参数的宏定义 和 带参数的宏定义
——不带参数的宏定义
1.一般形式:#define 宏名 字符串 【例】 #define ABC 10
2.作用:在编译预处理时,将源程序中所有"宏名"替换成右边的"字符串",常用来定义常量。
3.使用习惯和注意:宏名一般用大写字母,以便与变量名区别开来,但用小写也没有语法错误;对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作;在编译预处理用字符串替换宏名时,不作语法检查,只是简单的字符串替换。只有在编译的时候才对已经展开宏名的源程序进行语法检查;宏名的有效范围是从定义位置到文件结束。如果需要终止宏定义的作用域,可以用#undef命令;定义一个宏时可以引用已经定义的宏名。
#define PI 3.14
#define R 3.0
#define L 2*PI*R
#define S PI*R*R
/*
作用范围
/*
#undef PI
/*
之后PI不在进行字符串替换
*/
——带参数的宏定义
1.一般形式:#define 宏名(参数列表) 字符串
2.作用:在编译预处理时,将源程序中所有宏名替换成字符串,并且将 字符串中的参数 用 宏名右边参数列表 中的参数替换
3.使用习惯和注意:宏名和参数列表之间不能有空格,否则空格后面的所有字符串都作为替换的字符串;带参数的宏在展开时,只作简单的字符和参数的替换,要想计算得确保计算在宏定义中书写正确。
#include <stdio.h>
#define Pow(a) (a) * (a)
#define Pow_1(a) ( (a) * (a) )
int main(int argc, const char * argv[]) {
int b = Pow(10) / Pow(2); // int b = (10) * (10) / (2) * (2);
int b = Pow_1(10) / Pow(2); // int b = ( (10) * (10) ) / ( (2) * (2) );
printf("%d", b);
return 0;
}
宏定义和函数的区别
1.宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值问题
2. 函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行。所以带参数的宏比函数具有更高的执行效率
条件编译的概念——在很多情况下,我们希望程序的其中一部分代码只有在满足一定条件时才进行编译,否则不参与编译(只有参与编译的代码最终才能被执行)。
1.基本用法
#if 条件1 // 如果条件1成立,那么编译器就会把#if 与 #elif之间的code1代码编译进去
//(注意:是编译进去,不是执行,很平时用的if-else是不一样的)
...code1...
#elif 条件2 // 如果条件1不成立、条件2成立,那么编译器就会把#elif 与 #else之间的code2代码编译进去
...code2...
#else // 如果条件1、2都不成立,那么编译器就会把#else 与 #endif之间的code3编译进去
...code3...
#endif // 条件编译结束后,要在最后面加一个#endif,不然后果很严重
注意:#if 和 #elif后面的条件一般是判断宏定义而不是判断变量,因为条件编译是在编译之前做的判断,宏定义也是编译之前定义的,而变量是在运行时才有使用的意义。
2.其他用法
#if defined(MAX) // 如果前面已经定义过MAX这个宏,就将code编译进去。
...code...
#endif
#if !defined(MAX) // 如果前面没有定义过MAX这个宏,就将code编译进去。
...code...
#endif
// #ifdef的使用和#if defined()的用法基本一致
#ifdef MAX // 如果前面已经定义过MAX这个宏,就将code编译进去。
...code...
#endif
// #ifndef又和#if !defined()的用法基本一致
#ifndef MAX // 如果前面没有定义过MAX这个宏,就将code编译进去。
...code...
#endif
文件包含——#include,它可以将一个文件的全部内容拷贝另一个文件中。
1.一般形式:
第1种形式#include <文件名>
第2种形式 #include "文件名"
2.使用注意:
#include指令允许嵌套包含,比如a.h包含b.h,b.h包含c.h,但是不允许递归包含,比如 a.h 包含 b.h,b.h 包含 a.h。
使用#include指令可能导致多次包含同一个头文件,降低编译效率。防止多次包含同一头文件导致效率降低,使用条件编译。
#ifndef _ONE_H_ // 条件编译第一次运行 检查_ONE_H_ 是否被宏定义,没有就顺序执行;已经定义了就可以直接跳过
#define _ONE_H_ // 进行宏定义_ONE_H_
void one(); // 声明函数
#endif // 结束本次的条件编译