C语言(三)预处理指令

时间:2021-01-03 01:13:17

一、预处理指令

1.定义

C语言在对源程序进行编译之前,会先对一些特殊的预处理指令做解释,如:#include,产生一个新的源程序,这个过程称为“编译预处理”,之后再进行通常的编译工作。

2.作用范围

预处理指令可以出现在程序的任意位置,它的作用范围是从它出现的位置到文件末尾。习惯上,我们把预处理指令写在源程序开头。

3.类型

C语言提供了预处理指令主要有:宏定义、文件包含和条件编译。


二、不带参数的宏定义

1.一般形式

#define 宏名 字符串

如:#define A 10;

// 源程序中所有的宏名PI在编译预处理的时候都会被3.14所代替
#define PI 3.14
// 根据圆的半径计radius算周长
float girth(float radius) {
return 2 * PI *radius;
}
int main ()
{
float g = girth(2);
printf("周长为:%f", g);
return 0;
}


2.作用

在编译预处理的时候,将源程序中所有“宏名”替换成右边的字符串,常用来定义常量。

3.注意

1)宏名一般用大写字母,以便和变量名进行区别。

2)对程序中双引号括起来的字符串内的字符,不能进行宏的替换操作。

#define R 10
int main ()
{
char *s = "Radio"; //这里有R,但是不会替换
return 0;
}

3)在进行宏的替换操作时,不做语法的检查,只是简单的替换操作而已。只有在替换完后的下一步编译操作,才会进行语法检查。因此,如果有错误,也只会在编译的时候产生。

4)宏名的有效范围默认是从定义位置到文件末尾,如果想终止的话,可以使用“#undef 宏名“,这样,宏名的有效范围就缩减至终止操作的位置。

5)定义宏时,可以使用已经定义过的宏名。

#define R  3.0
#define PI 3.14
#define L 2*PI*R
#define S PI*R*R

三、带参数的宏定义

1.一般形式

#define 宏名(参数列表) 字符串

2.作用

在编译预处理的时候,将源程序中的宏名替换成字符串,并将字符串中的参数用源程序中的宏名的实际数值代替。

3.注意

1)宏名和参数列表之间不能有空格。

2)定义宏时,参数列表有"()"括起来,字符串的参数也用"()"括起来

#include <stdio.h>
#define D(a) 2*a
int main ()
{
int b = D(3+4);

printf("%d", b);
return 0;
}
//2*3+4
//输出结果:10
#include <stdio.h>#define D(a) 2*(a)int main (){    int b = D(3+4);        printf("%d", b);    return 0;}//2*(3+4)//输出结果:14

3)计算结果也用”()“括起来

#include <stdio.h>
#define Pow(a) (a) * (a)
int main(int argc, const char * argv[]) {
int b = Pow(10) / Pow(2);

printf("%d", b);
return 0;
}
//10*10/2*2=100
//输出结果:100
#include <stdio.h>#define Pow(a) ((a) * (a))int main(int argc, const char * argv[]) {    int b = Pow(10) / Pow(2);        printf("%d", b);    return 0;}//(10*10)/(2*2)=25//输出结果:25


4.宏与函数的区别

虽然带参数的宏定义和函数十分相似,但是还是有本质的区别:

1)宏定义不涉及存储空间的分配、参数类型分配、参数传递、返回值的问题。

2)函数在程序运行时执行,而宏替换在编译预处理时执行。所以带参数的宏比函数效率更高。


四、条件编译

1.概念

 在很多情况下,我们希望程序的其中一部分代码只有在满足一定条件下才进行编译,否则不参与编译(只有参与编译的代码最终才能被执行),这就是条件编译。

2.基本用法

#if 条件1
code1
#elif 条件2
code2
#else 条件3
code3
#endif
    1)条件1成立,编译器就会把code1代码编译进去。
    2)code2,code3同理
    3)条件编译结束后,一定要在后面加上 #endif,不然后果很严重。
    4)#if 和 #elif 后面的条件一般是判断宏定义而不是判断变量。因为条件编译是在编译之前做的判断,宏定义也是在编译之前定义的,而变量是在运行时才产生的,才有使用的意义。

3.例子

#include <stdio.h>
#define MAX 11; //宏定义
int main(){
#if MAX ==0
printf("MAX是0");
#elif MAX >0
printf("MAX大于0");
#else
printf("MAX小于0");
#endif
return 0;
}
在预处理后的代码为:

/*stdio.h文件中的内容将会代替#include <stdio.h>的位置*/
int main ()
{
printf("MAX大于0");
return 0;
}

4.其它用法

1)#if defined()和#if !defined()的用法
    在编译预处理阶段产生效果。
①#if defined()...#endif    
#include <stdio.h>
#define MAX 11; //宏定义
int main(){
#if defined(MAX)
printf("定义了宏变量");
#endif

return 0;
}
用来判断是否定义过宏变量。注意一定要添加 #ednif。

#if !defined()...#endif

#include <stdio.h>
#define MAX 11; //宏定义
int main(){
#if !defined(MA)
printf("没有该宏定义");
#endif
return 0;
}
    没有定义过该宏才将编译条件语句。

2)#ifdef和#ifndef的用法
    #ifdef和 #if defined()用法一致,一个有括号,一个没括号。
①#ifdef...#endif
#include <stdio.h>
#define MAX 11; //宏定义
int main(){
#ifdef MAX
printf("有宏定义");
#endif
return 0;
}
②#ifndef...#endif
#include <stdio.h>#define MAX 11;	//宏定义int main(){		#ifndef MA			printf("没有宏定义");		#endif		return 0;}

五、文件包含

1.常见形式
    #include <文件名>
    #include "文件名"
2.使用注意
1)#include指令允许嵌套包含,但不允许递归包含
    嵌套包含:a.h包含b.h,b.h包含c.h
    递归包含:a.h包含b.h,b.h包含a.h
2)重复包含同一个头文件,导致重复定义错误。
    如:在one.h中定义了void one()。在two.h中包含了one.h,并定义了two()。
         现在在main.c中包含了one.h,two.h。
    上面的情况就出现了重复定义的错误。
    为了解决这种重复包含同一个头文件的错误,一般我们会这样写头文件。

3.例子
one.h
#ifndef _ONE_H_
#define _ONE_H_

void one(){
printf("this is method one()\n");
}
#endif
two.h
#ifndef _TWO_H_
#define _TWO_H_

#include "one.h"
void two(){
printf("this is method two()\n");
}
#endif
first.c
#include <stdio.h>#include "one.h"#include "two.h"int main(){	one();	two();	return -0;}