编译预处理是指在c编译程序对c源程序进行编译之前,对预处理命令进行“预先“处理的过程。编译预处理是通过编译预处理程序实现的。
预处理命令不是c语言的组成部分,c语言的编译程序无法识别他们。
比如#include<stdio.h>就是一个预处理命令,其功能是在将源程序编译成目标程序之前,将文件“stdio.h“中的内容替换该命令,然后由编译程序将源程序翻译成目标程序。
C语言中的预编译处理命令主要有以下3种:宏定义、文件包含、条件编译
为了与一般的c语句相区分,编译预处理命令必须以#为首字符、尾部不能加分号结尾(c语句必须加分号),一行只能写一条编译预处理命令。
宏定义
不带参数的宏定义
#define 宏名 字符串
功能:在编译预处理的时候,将程序种该命令后所有与宏名相关的文本用字符串置换。
使用宏定义,可以用一个简单的名字(宏名)来代替一个较长的字符串,以增加程序的可读性。
在编译预处理时将宏名替换成字符串的过程称为“宏展开“。
以下时关于宏定义和宏展开的说明:
- 宏名一般习惯用大写字母表示,以便与一般变量相区分。但是这并非规定,也可以用小写字母。
- 一个宏名只能被定义一次,否则会出错,被认为是重复定义。
- 在进行宏定义时,可以引用已定义的宏名,可以层层替换
- 对在字符串常量以及用户标识符中,与宏名相同的部分不做替换。
- 宏定义的作用域:从定义开始到程序结束
- 若宏定义在一行中写不下时,换行时,需要在行尾加上字符\
带参数的宏定义
#define 宏名(形参表) 字符串
形参表中的不同形参之间用逗号隔开,字符串中包含形参表中的参数。
在编译预处理时,将程序中该命令后所有与宏名相关的文本用字符串替换,但是替换时字符串中的形参要用相应的实参替换。
带参数的宏与函数形式相似但是本质不同。区别如下:
- 函数调用时先求实参表达式的值,然后传给形参。而使用带参数的宏只是进行简单的字符替换。
- 函数调用实在程序运行时处理的,分配临时内存单元。而宏展开则是在预处理时(编译之前)进行的,在展开时并不分配内存单元。
- 在使用带参数的宏时,要注意括号的使用。
在程序设计时,经常把那些反复使用的运算表达式定义为带参数的宏。
定义带参数的宏时,对形参的数量没有限制。但是一般情况下以不超过3个为宜。
终止宏定义
#undef 宏名
文件包含
文件包含预处理命令有以下两种格式:
#include”文件名”
#include<文件名>
功能:在编译预处理时,把命令中指定的文件内容复制到该命令处,使得指定文件de额内容成为当前源文件的一部分。
执行第一种格式的命令的时候,需要现在当前目录中查找,如果没有找到再到系统指定的标准目录下去查找(通常在#include目录下)
执行第二种格式的命令时,则只到系统指定的标准命令下查找。
一般来说,如果为调用库函数而用#include命令来包含相关的头文件,则用尖括号,以节省查找时间。如果要保护的是用户自己编写的文件(这种文件一般在当前目录中),一般使用双引号。若文件不在当前目录中,则可以在双引号内给出文件路径。
在程序设计时,可以把一批常用的符号常量、函数说明、宏定义以及某些有用的数据类型说明和类型定义,组织在一个独立的文件中,在程序需要使用这些信息的时候,可以用#include命令把他们包含到所需要的位置上面去。
在使用#include命令时还需要注意以下2点:
- 每个#include命令只能包含一个文件。
- 文件包含嵌套,即包含一个被包含的源文件可以包含另一个源文件
条件编译
一般情况下,源程序的所有行都要参加编译.
条件编译:当条件成立时去编译一组语句,而当条件不成立时编译另一组语句.
条件编译命令有3种形式:
1,#ifdef标识符
程序段1
#else
程序段2
#endif
功能:当指定的标识符在此之前已经被#define语句定义过时,程序段1就被编译,否则,程序段2被编译.其中#else的内容可以被省略.
比如,在调试程序的时候经常需要输出一些信息,一旦调试结束,这些信息也就不再需要了.
2,#ifndef标识符
程序段1
#else
程序段2
#endif
功能:当指定的标识符在此之前,没有被#define语句定义过,程序段1就被编译,否则程序段2被编译.
3,#if表达式
程序段1
#else
程序段2
#endif
功能:如果指定的表达式的值为真(非0)则编译程序段1,否则编译程序段2.