一、C语言提供了多种编译预处理功能,其中主要的有宏定义、文件包含、条件编译。
预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。下一步,此输出文件将作为编译程序的输出而被翻译成为机器指令。
指令 用途
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码,其实就是else if的简写
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息
#define ONE 1
#define TWO 2
#define SUM(ONE+TWO)
一是注意上面的宏定义使用了括号。尽管它们并不是必须的。但出于谨慎考虑,还是应该加上括号的。例如:
six=THREE*TWO;
预处理过程把上面的一行代码转换成:
six=(ONE+TWO)*TWO;
如果没有那个括号,就转换成six=ONE+TWO*TWO;了。
也就是说预处理仅是简单的字符替换,要时刻注意这一点,很多错误都会因此出现。
二是虽然我们举例用了#define ONE 1 这个例子,但是一般要求宏定义要有其实际意义,#define ONE 1这种没意义的宏定义是不推荐的。
1.3宏还可以代表一个字符串常量,例如:#define VERSION "Version 1.0 Copyright(c) 2003"
带参数的宏和函数调用看起来有些相似。看一个例子:
#define Cube(x) (x)*(x)*(x)
int num=8+2;
volume=Cube(num);
下面的用法是不安全的:
volume=Cube(num++);
如果Cube是一个函数,上面的写法是可以理解的。但是,因为Cube是一个宏,所以会产生副作用。这里的书写不是简单的表达式,它们将产生意想不到的结果。它们展开后是这样的:
volume=(num++)*(num++)*(num++);
很显然,结果是10*11*12,而不是10*10*10;
那么怎样安全的使用Cube宏呢?必须把可能产生副作用的操作移到宏调用的外面进行:
int num=8+2;
volume=Cube(num);
num++;
宏函数使用不当会出现一些难以发现的错误,请慎重使用。
1.5 #运算符出现在宏定义中的#运算符把跟在其后的参数转换成一个字符串。有时把这种用法的#称为字符串化运算符。例如:
#define PASTE(n) "adhfkj"#n
int main()
{
printf("%s\n",PASTE(15));
return 0;
}
//输出adhfj15
宏定义中的#运算符告诉预处理程序,把源代码中任何传递给该宏的参数转换成一个字符串。所以输出应该是adhfkj15。
##运算符用于把参数连接到一起。预处理程序把出现在##两侧的参数合并成一个符号。看下面的例子:
#define NUM(a,b,c) a##b##c
#define STR(a,b,c) a##b##c
int main()
{
printf("%d\n",NUM(1,2,3));
printf("%s\n",STR("aa","bb","cc"));
return 0;
}
//最后程序的输出为:
123
aabbcc
2.文件包含
采用头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用。因为在需要用到这些定义的C源程序中,只需加上一条#include语句即可,而不必再在此文件中将这些定义重复一遍。预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。
#include预处理指令的作用是在指令处展开被包含的文件。包含可以是多重的,也就是说一个被包含的文件中还可以包含其他文件。标准C编译器至少支持八重嵌套包含。预处理过程不检查在转换单元中是否已经包含了某个文件并阻止对它的多次包含,这个的处理办法上面已经给出。
#endif用于终止#if预处理指令。
#else指令用于某个#if指令之后,当前面的#if指令的条件不为真时,就编译#else后面的代码。
#define DEBUG //此时#ifdef DEBUG为真
//#define DEBUG 0 //此时为假
int main()
{
#ifdef DEBUG
printf("Debugging\n");
#else
printf("Not debugging\n");
#endif
printf("Running\n");
return 0;
}
这样我们就可以实现debug功能,每次要输出调试信息前,只需要#ifdef DEBUG判断一次。不需要了就在文件开始定义#define DEBUG 。 #elif预处理指令综合了#else和#if指令的作用。
#define TWO
int main()
{
#ifdef ONE
printf("1\n");
#elif defined TWO
printf("2\n");
#else
printf("3\n");
#endif
}
//输出结果是2。
3.2 #ifdef和#ifndef
这二者主要用于防止重复包含。我们一般在.h头文件前面加上这么一段:
//头文件防止重复包含
//funcA.h
#ifndef FUNCA_H
#define FUNCA_H
//头文件内容
#end if
这样,如果a.h包含了funcA.h,b.h包含了a.h、funcA.h,重复包含,会出现一些type redefination之类的错误。
#if defined等价于#ifdef; #if !defined等价于#ifndef
4、特殊符号。
预编译程序可以识别一些特殊的符号。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。
4.1 __LINE__
#include<stdio.h>
int main()
{
printf("Hello World!\n");
printf("%s\n",__FILE__);
printf("%d\n",__LINE__);
return 0;
}
4.2 #line等
#error指令将使编译器显示一条错误信息,然后停止编译。
#line指令改变_LINE_与_FILE_的内容,它们是在编译程序中预先定义的标识符。
#pragma指令没有正式的定义。编译器可以自定义其用途。典型的用法是禁止或允许某些烦人的警告信息。
//#line举例
#line 100 //初始化行计数器
#include<stdio.h> //行号100
int main()
{
printf("Hello World!\n");
printf("%d",__LINE__);
return 0;
}
//输出104
转自:http://www.cnblogs.com/rusty/archive/2011/03/27/1996806.html