程序的编译之预处理详解

在之前的文章中介绍了程序的编译和链接这两个过程(详见http://t.csdn.cn/39SPf),那么这篇文章我就带大家来详细的了解一下预处理这个过程。

目录

​ 一、#define

二.#和##

三.命令行定义

四.条件编译

五.文件包含 

首先,大家先要了解一些可能需要用到的预定义的符号:

程序的编译之预处理详解_第1张图片 一、#define

1.#define 定义标识符

#define name stuff

如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。我们要注意的是书写完stuff的时候,除非需要否则不用加;否则就会出现下面这种情况:

程序的编译之预处理详解_第2张图片 

2. #define 定义宏

(1)首先我们来看一下宏与函数的优缺点

优点:因为调用函数所需要的的代码可能比用宏计算所需要的时间更多,所以宏比函数在程序的规模和速度方面更胜一筹,而且宏的使用并不需要声明类型,所以宏能够使用的地方更多,这些是他的好处。

缺点:宏不能调试,不能递归,而且由于不用声明类型,所以使用时不够严谨,宏可能会带来运算符优先级的问题,导致程容易出现错。

不过宏可以做到一些函数做不到的事,比如宏的参数可以是类型:

#define A(n,type*) (type*)(n,sizeof(type*)

int main()
{
	int*n = NULL;
	n = A(10,int);
	return 0;
}

(2)带副作用的宏参数 :

#define MAX(a, b) ( (a) > (b) ?(a) : (b) )
int main()
{
	int x = 5;
	int y = 8;
	int z = MAX(x++, y++);
	printf("x=%d y=%d z=%d\n", x, y, z);
}

由于每一次的调用a或b都会++,所以导致结果为x=6 y=10 z=9,因此在使用宏的时候应避免使用++的形式而是改为+1的形式。

#define SQUARE(x) x*x
int main()
{
int a = 5;
printf("%d\n" ,SQUARE( a + 1) );
return 0;
}

我们乍一看这段代码的结果是36,然而实际运行的结果却是11,造成这个结果的原因是替换时x被替换为a+1,所以替换后的结果就是a+1*a+1。所以我们在写宏的时候要注意括号的使用,因此如果要使这道题的结果为36的话,最好改为为: #define  SQUARE(x) ((x)*(x))

(3)如果我们要移除一个宏定义可以使用#undef

二.#和##

1.使用 # ,把一个宏参数变成对应的字符串。

程序的编译之预处理详解_第3张图片

2.##可以把位于它两边的符号合成一个符号。 它允许宏定义从分离的文本片段创建标识符。如:程序的编译之预处理详解_第4张图片 

三.命令行定义

许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。

例如:gcc -D ARRAY_SIZE=10 programe.c

四.条件编译

在编写代码时,我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件 编译指令。

1.
#if 常量表达式
 //...
#endif
如:
#define __DEBUG__ 1
#if __DEBUG__
 //..
#endif
2.多个分支的条件编译
#if 常量表达式
 //...
#elif 常量表达式
 //...
#else
 //...
#endif
3.判断是否被定义
#if defined(symbol)//如果定义了symbol
#ifdef symbol      //同if defined
#if !defined(symbol)
#ifndef symbol

五.文件包含 

1.头文件的包含:

#include后用“”的查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标 准位置查找头文件。

#include后用<>的查找策略:查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。

2.嵌套文件包含

我们在写一个大的项目的时,很有可能遇到下面这个情况:

程序的编译之预处理详解_第5张图片

 为了避免出现头文件的重复包含的情况我们可以使用下面的这两个代码:

#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif   //__TEST_H__
#pragma once

 就可以避免头文件的重复引入了。

求点赞!!

你可能感兴趣的:(c语言)