第十一章-预处理指令

宏定义

不带参数的宏定义

//定义一个宏时可以引用已经定义的宏名
#define R  3.0
#define PI 3.14
#define L  2*PI*R
#define S  PI*R*R

#define COUNT 4
    int ages[COUNT] = {1, 2, 67, 89};
    for ( int i = 0; i<COUNT; i++) {
        printf("%d\n", ages[i]);
    } 
    // 从这行开始,COUNT这个宏就失效
#undef COUNT
    

带参数的宏

 1 #include <stdio.h>
 2 
 3 #define average(a, b) (a+b)/2
 4 
 5 int main ()
 6 {
 7     int a = average(10, 4);
 8     
 9     printf("平均值:%d", a);
10     return 0;
11 }
 3行中定义了一个带有2个参数的宏average,第7行其实会被替换成:int a = (10 + 4)/2;
 

使用注意

1 #include <stdio.h>
 2 
 3 #define D(a) 2*a
 4 
 5 int main ()
 6 {
 7     int b = D(3+4);
 8     
 9     printf("%d", b);
10     return 0;
11 }

7行将被替换成int b = 2*3+4;
如果定义宏的时候用小括号括住参数,把上面的第3行改成:`#define D(a) 2*(a)`

7行将被替换成int b = 2*(3+4);

1 #include <stdio.h>
 2 
 3 #define Pow(a) (a) * (a)
 4 
 5 int main(int argc, const char * argv[]) {
 6     int b = Pow(10) / Pow(2);
 7     
 8     printf("%d", b);
 9     return 0;
10 }
6行代码被替换为:
int b = (10) * (10) / (2) * (2);
将上面的第3行代码改为:#define Pow(a) ( (a) * (a) )
那么第6行被替换为:
int b = ( (10) * (10) ) / ( (2) * (2) );

与函数的区别

条件编译

1 #if 条件1
2  ...code1...
3 #elif 条件2
4  ...code2...
5 #else
6  ...code3...
7 #endif
如果条件1成立,那么编译器就会把#if  #elif之间的code1代码编译进去(注意:是编译进去,不是执行,很平时用的if-else是不一样的)
 1 #include <stdio.h>
 2 
 3 #define MAX 11
 4 
 5 int main ()
 6 {
 7 #if MAX == 0
 8     printf("MAX是0");
 9 #elif MAX > 0
10     printf("MAX大于0");
11 #else
12     printf("MAX小于0");
13 #endif
14     return 0;
15 }
	#ifdef的使用和#if defined()的用法基本一致
	 #ifdef MAX
	2     ...code...
	3 	 #endif
    如果前面已经定义过MAX这个宏,就将code编译进去。
    #ifndef又和#if !defined()的用法基本一致
    1 #ifndef MAX
	2     ...code...
	3 	#endif
如果前面没有定义过MAX这个宏,就将code编译进去。

文件包含

使用注意

  1. #include指令允许嵌套包含,比如a.h包含b.h,b.h包含c.h,但是不允许递归包含,比如 a.h 包含 b.h,b.h 包含 a.h。
  2. 使用#include指令可能导致多次包含同一个头文件,降低编译效率
    • 在one.h中声明了一个one函数;在two.h中包含了one.h,顺便声明了一个two函数。
    • 假如我想在main.c中使用one和two两个函数,而且有时候我们并不一定知道two.h中包含了one.h,所以可能会这样做:
     #include"one.h" 
     #include"two.h"
    
    • 编译预处理之后main.c的代码是这样的:
     1 void one();
     2 void one();
     3 void two();
     4 int main ()
     5 {
     6 
     7     return 0;
     8 }
    
    • 可以看出来,one函数被声明了2遍,根本就没有必要,这样会降低编译效率。
    • 为了解决这种重复包含同一个头文件的问题,一般我们会这样写头文件内容:
	one.h文件
	
  9   #ifndef _ONE_H_
  10	#define _ONE_H_
  11	//声明了一个one函数
  12 	void one();
  13 	#endif
	
	two.h文件
	
  9  #ifndef _TWO_H_
 10  #define _TWO_H_
 11  #include "one.h"
 12	  //声明了一个two函数
 13  void two();
 14	  #endif

解析:大致解释一下意思,就拿one.h为例:当我们第一次#include “one.h”时,因为没有定义_ONE_H_,所以第9行的条件成立,接着在第10行定义了_ONE_H_这个宏,然后在12行声明one函数,最后在13行结束条件编译。当第二次#include “one.h”,因为之前已经定义过_ONE_H_这个宏,所以第9行的条件不成立,直接跳到第13行的#endif,结束条件编译。就是这么简单的3句代码,防止了one.h的内容被重复包含。

	#include "one.h"
	#include "two.h"
替换成下面
 1 // #include "one.h"
 2 #ifndef _ONE_H_
 3 #define _ONE_H_
 4 
 5 void one();
 6 
 7 #endif
 8 
 9 // #include "two.h"
10 #ifndef _TWO_H_
11 #define _TWO_H_
12 
13 // #include "one.h"
14 #ifndef _ONE_H_
15 #define _ONE_H_
16 
17 void one();
18 
19 #endif
20 
21 void two();
22 
23 #endif

编译结果

 void one();
 void two();
Table of Contents