1.3.3条件编译
有一次,我和几个朋友一起在看威尔史密斯主演的《我是传奇》,其实我早就看过了这个电影,但是其他人都没有看过。我决定戏弄一下他们,于是在剧情快进到最后的时候,我说出了结局威尔史密斯是如何在最后以自己的生命换回了人类的希望。
可是就在电影的最后,威尔史密斯居然没有按照剧情和变异人们同归于尽,而是开上了小汽车去开始了新的生活。天啊,这到底是怎么回事?之前我看的结局明明是威尔史密斯挂了的啊。就在我怀疑自己得了严重的精神疾病的时候,这时我的一个朋友转过头来冲着我说“你以为导演就不会使用条件编译了吗”
问题描述:
我们经常会因为有些电影里出人意料的结局而惊叹编剧的想象力。在整个的欣赏过程中,我们每个人可能会对结局有不同的猜测,有时甚至会争论起来,直到看到结局才会分出一个胜负。
后来我才知道其实这种猜测往往是毫无意义的,因为很多电影根本就拍摄了不止一个结局,然后在播出前,再根据具体的情况,决定最后采用哪一种结局播出。
实际上,我们写的程序往往也有类似的情形。对于一个特定的功能,可以写两段不同的实现代码A和B,在编译的时候,根据不同的情形选择A或者B进行编译。
当完成一整段程序的时候,在执行的时候,可能的代码段不需要执行,而有的代码段可能需要在某些条件成立时执行,不成立时就不执行,这种行为就是条件编译。
其中最常见的两类命令就是:
1.#if、#else、#elif和#endif指令
2.#ifdef和#ifndef
实例分析:
当需要对一段代码进行选择性的编译时,通常很多人会使用注释的方式,把这些代码注释掉。这里我们可以采用另一种方式比如下面的这段代码:
通过#if和#endif就可以对程序进行有选择的编译。
/*example1_3_10.c*/ #include<stdio.h> #define PI 3.1415926 #define ComputeArea( x) (PI*x*x) int main(void) { #if 0 float Radius,Area; scanf("%f",&Radius); /*输入半径的值*/ Area= ComputeArea(Radius);/*调用了函数ComputeArea(float x)*/ printf("%f\n",Area); /*输出圆的面积*/ #endif return 0; } |
#if和#endif这两句之间的代码就不会得到执行,而是在编译的时候被忽略掉。如果宏条件符合的话,才会编译之后的代码,否者的话,之后的代码相当于被注释掉了一样。和常见的选择结构一样,if有自己的else和else if一样,#if也有自己的#else和#elif,最为常见的条件编译方式有以下三种:
第一种常见用法:
#if 表达式
程序段1
#else
程序段2
#endif
它的作用是:当指定的表达式值为真(非零)时就编译程序段1,否则编译程序段2。可以事先给定一定条件,使程序在不同的条件下执行不同的功能。
比如我们在计算图形面积的时候,有的时候可能遇见的不一定是圆形,也可能是矩形,如果希望一个程序能同时完成这两项工作,并且在这两项工作中切换,就可以写成如下这个程序
/*example1_3_11.c*/ #define FLAG 1 #include<stdio.h> #define PI 3.1415926 #define ComputeCircleArea( x) (PI*x*x) #define ComputeRectangleArea( x,y) (x*y) Int main(void) { #if FLAG float Radius,Area; scanf("%f",&Radius); /*输入半径的值*/ Area= ComputeCircleArea Radius);/*调用了函数*/ printf("%f\n",Area); /*输出圆的面积*/ #else float Length, Width,Area; scanf("%f,%f",&Length, &Width); /*输入半径的值*/ Area= ComputeRectangleArea(Length, Width);/*调用了函数*/ printf("%f\n",Area); /*输出矩形的面积*/ #endif return 0; } |
这段程序的功能可以通过FLAG的值进行切换,直接定义为1,该函数计算的是圆的面积,把FLAG的值改为0,就可以用来计算矩形的面积。
第二种常见用法:
#ifdef 标识符
代码段1
#else
代码段2
#endif
这里举出一个例子
/*example1_3_12.c*/ #define FLAG 1 #include<stdio.h> #define PI 3.1415926 #define ComputeCircleArea( x) (PI*x*x) #define ComputeRectangleArea( x,y) (x*y) Int main(void) { #ifdef PI float Radius,Area; scanf("%f",&Radius); /*输入半径的值*/ Area= ComputeCircleArea Radius);/*调用了函数*/ printf("%f\n",Area); /*输出圆的面积*/ #else printf(“Sorry,The PI is not defined”); /*输出矩形的面积*/ #endif return 0; } |
第三种常见用法:
#ifndef 标识符
代码段1
#else
代码段2
#endif
详细的例子如下:
/*example1_3_13.c*/ #define FLAG 1 #include<stdio.h> #define PI 3.1415926 #define ComputeCircleArea( x) (PI*x*x) #define ComputeRectangleArea( x,y) (x*y) Int main(void) { #ifndef PI printf(“Sorry,The PI is not defined”); /*输出矩形的面积*/ #else float Radius,Area; scanf("%f",&Radius); /*输入半径的值*/ Area= ComputeCircleArea Radius);/*调用了函数*/ printf("%f\n",Area); /*输出圆的面积*/ #endif return 0; } |
第三种用法其实只是第二种用法的一种变形,#ifdef是定义了这个标识符的话执行代码段1,而#ifndef则是没有定义这个标识符的话才执行代码段1。
总结
这里的条件编译其实在程序中直接使用选择语句其实可以达到同样的功能,但是预编译命令是在预编译阶段执行的,而选择语句是在编译阶段进行的,采用预编译命令的好处是很多语句都可以不用被编译,比如example1_3_10.c中的
float Length, Width,Area;
scanf("%f,%f",&Length, &Width); /*输入半径的值*/
Area= ComputeRectangleArea(Length, Width);/*调用了函数*/
printf("%f\n",Area); /*输出矩形的面积*/
这些语句只是在编写的时候出现,实际上并不会被编译,也就不需要编译器它们转化成汇编代码,这样做的好处是可以减少系统的开销。