一、预处理指令简介
1、所有的预处理指令都是以#开头,且结尾没有分号
2、预处理指令在代码翻译成0和1之前执行
3、预处理指令可以出现在程序的任何位置,它的作用范围是从它出现的位置到文件尾。习惯上我们尽可能将预处理指令写在源程序开头,这种情况下,它的作用范围就是整个源程序文件
4、C语言提供的预处理指令主要有:宏定义、文件包含、条件编译
二、宏定义
1、不带参数的宏
(1)定义格式:#define 宏名值
-
宏名一般大写或者以k开头,以便与变量名区别开来,但小写也没有语法错误
-
宏仅是文本替换
-
右边的值也可以省略,比如#defineABC
-
作用域:从编写指令的那一行开始到文件结束,也可以用#undef取消宏定义作用
(2)作用
它的作用是在编译预处理时,将源程序中所有"宏名"替换成右边的"值",常用来定义常量。对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作。
#define COUNT 5
#define kCount 5
会将COUNT替换为5,双引号内的不会替换,如:char *name=”COUNT”;
#undef COUNT // 此行开始COUNT这个宏失效
(3)在编译预处理用字符串替换宏名时,不作语法检查,只是简单的字符串替换。只有在编译的时候才对已经展开宏名的源程序进行语法检查
(4)定义一个宏时可以引用已经定义的宏名
#define R 3.0
#define PI 3.14
#define L 2*PI*R
2、带参数的宏
一般形式:#define 宏名(参数列表) 字符串
作用:在编译预处理时,将源程序中所有宏名替换成字符串,并且将字符串中的参数用宏名右边参数列表中的参数替换
-
宏名和参数列表之间不能有空格,否则空格后面的所有字符串都作为替换的字符串
#define sum(v1, v2) v1+v2
int c=sum(10,11); // 将sum(10,11)转换为10+11,即c值为21
int d=sum(2,3)*sum(4,5);//d的值为19,不是预想的45
分析:带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何计算操作,所以sum(2,3)*sum(4,5)会转换为2+3*4+5,即19,并不会先计算出sum(2,3),再去乘sum(4,5)的值。所以定义时结果值要加小括号,参数也要加小括号。
#define sum(v1, v2) ((v1)+(v2))
#define pingfang(a) ((a)*(a))
3、与函数的区别
带参数的宏定义,在源程序中出现的形式与函数很像。但是两者是有本质区别的:
(1)宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值问题
(2)函数调用在程序运行时执行,而宏的替换只在编译预处理阶段进行。所以带参数的宏比函数具有更高的执行效率,简单函数可以使用宏代替。
三、条件编译
1、概念
有时我们希望程序的某一部分代码只有在满足一定条件时才进行编译,否则不参与编译(只有参与编译的代码最终才会被执行),这就是条件编译。简单的说条件编译就是有条件的编译某段代码,
2、用法
#if 条件1
//...code1...
#elif 条件2
//...code2...
#else
//...code3...
#endif
(
1
)如果条件
1
成立,那么编译器就会把
#if
与
#elif
之间的
code1
代码编译进去(注意:是编译进去,不是执行),其他代码不会参与编译。而
if-else
会编译所以语句
(2)如果条件1不成立、条件2成立,那么编译器会把code2代码编译进去
(3)如果条件1、2都不成立,那么编译器就会把#else与 #endif之间的code3编译进去
(4)条件编译结束后,必须在最后面加一个#endif
(5) #if 和 #elif后面的条件一般是判断宏定义而不是判断变量,因为条件编译是在编译之前做的判断,宏定义也是编译之前定义的,而变量是在运行时才产生的、才有使用的意义
3、#if defined()和#if!defined()的用法
#if 和 #elif后面的条件不仅仅可以用来判断宏的值,还可以判断是否定义过某个宏。比如:
#if defined(MAX)
// ...code...
#endif
如果前面已经定义过
MAX
这个宏,就将
code
编译进去。它不会管
MAX
的值是多少,只要定义过
MAX
,条件就成立。
条件也可以取反:
#if !defined(MAX)
//...code...
#endif
如果前面没有定义过
MAX
这个宏,就将
code
编译进去。
4、#ifdef和#ifndef的使用
#ifdef的使用和#if defined()的用法基本一致
#ifdef MAX
//...code...
#endif
如果前面已经定义过
MAX
这个宏,就将
code
编译进去。
#ifndef又和#if !defined()的用法基本一致
#ifndef MAX
//...code...
#endif
如果前面没有定义过
MAX
这个宏,就将
code
编译进去。
5、示例
#include <stdio.h>
#define MAX 10
int main ()
{
#if MAX == 0
printf("MAX是0");
#elif MAX > 0
printf("MAX大于0");
#else
printf("MAX小于0");
#endif
return 0;
}
在第
2
行定义了一个宏
MAX
,注意第
5
到第
11
行的条件编译语句。由于
MAX
为
10
,所以
#elif
的条件成立,第
8
行代码将会被编译进去,其实编译预处理后的代码是这样的:
int main ()
{
printf("MAX大于0");
return 0;
}
四、文件包含
1、作用
常见的#include就是文件包含,它是拷贝<>或””内的文件到#include处
2、一般形式
#include <文件名>
用于系统自带的文件,直接在C语言库函数的头文件所在的目录中寻找文件
#include "文件名"
用于自定义的文件,系统会先在源程序当前目录下寻找,若找不到,再到操作系统的path路径中查找,最后才到C语言库函数头文件所在目录中查找
3、使用注意
(1)#include指令允许嵌套包含,比如a.h包含b.h,b.h包含c.h,但是不允许循环包含,比如 a.h 包含 b.h,b.h 包含 a.h。
(2)使用#include指令可能导致多次包含同一个头文件,这样编译效率会降低,利用条件编译可防止头文件重复包含
#dendef 文件名_H
#define 文件名_H//定义一个空的宏
//函数声明
#endif
两个.h文件内的宏名不可以相同,否则可能导致另一个头文件不被编译,所以宏名以文件名_H命名
例如:
#dendef ONE_H
#define ONE_H
void one();
#endif
当我们第一次#include "one.h"时,因为没有定义ONE_H,所以条件成立,所以会拷贝one.h文件,会定义ONE_H 这个宏,声明one函数。当第二次#include"one.h",因为已经定义过ONE_H这个宏,所以条件不成立,直接跳到#endif,结束条件编译。所以防止了one.h的内容被重复包含。