预处理器

在编译器把程序换换成目标代码之前,先由C预处理器处理源文件:
C源代码——>预处理器——>编译器

#include 包含一个文本文件的内容
#undef 取消之前定义的某个宏
#define 定义宏
#if 如果条件满足,执行指定动作
#ifdef 如果已经定义了某个宏,执行指定动作
#ifndef 如果没有定义某个宏,执行指定动作
#else 如果先前的#if、#ifdef或#ifndef不成立,执行指定动作
#endif 作为#if、#ifdef或#ifndef命令动作范围的结束标记
#elif else if
#line 设置当编译器发出警告或错误信息时所使用的行号
#error 指定编译期间的错误及对应信息
#pragma 给编译器提供实现细节信息
# 忽略此行

#include <stdio.h>
尖括号表示在操作系统已知的目录中寻找该文件,预处理器在上述目录中找指定文件,找到后就用该文件的内容替换#include行。而用双括号括起的文件名,表示在工作目录(一般是当前目录)中寻找被包含的文件。
 

考虑下面一系列的#define命令,它说明了如何在随后的宏定义中使用一个已定义的宏。
#define NormalMeeting 3
#define SpecialMeeting 2
#define TimePerMeeting 20
#define TotalMeeting NormalMeeting+SpecialMeeting
#define TotalTime TotalMeeting*TimePerMeeting
TotalTime的值时43,而不是100。宏TotalMeeting展开成NormalMeeting+SpecialMeeting,这些宏又展开成3+2.
因此TotalTime展开胃3+2*20=43
在定义TotalMeeting时,可以加括号来避免这个问题#define TotalMeeting (NormalMeeting+SpecialMeeting)

通过第二条#define命令来改变某个宏的定义是非法的,但重复定义某条#define命令是合法的。
例如,头文件stdio.h中包含了#define TRUE 1
那么如下编写仍然是合法的:
#include <stdio.h>
#define TRUE 1

带参数的宏
定义宏时可以带参数,这些参数相当于实际参数的占位符。带参数的宏也是#define开始,接下来是宏的名字、包含参数的括号,参数由逗号分隔。在宏的名字和左括号之间不允许有空格。例如:
#define PRINT3(e1,e2,e2) printf("/n%c/t%c/t%d",(e1),(e2),(e3))

带参数的宏与函数在语法上是相似的,但它们之间存在重要的差异。
#define min(x,y) ((x)<(y)?(x):(y))
int max(int x,int y) { return (x>y)?x:y;}
min是一个带参数宏,但没有传任何实参给min,min也没有返回任何值,实际上,预处理器只是把min(num1,num2)在代码被真正编译前替换成((num1)>(num2))?(num1):(num2)
max的使用时函数调用,在程序执行时函数调用会有相关的系统开销,因为系统必须复制传递给函数的所有实参,并记录下在函数返回时继续执行的地址等。而宏没有这种运行时的系统开销,因此在这个方面宏比函数效率高。
宏和函数之间还有其他一些细微区别。宏不进行参数类型检查,有时这是一个优点,例如宏min可以在任意数值数据类型的两个值中找出较小值。但缺乏类型检查也会产生一些难以发现的错误。
宏也可能产生不可预知的副作用。考虑#define square(x) (x)*(x)
当下面的代码被处理时:
a=3;
b=square(a++):
第二行代码被展开成b=(a++)*(a++);在不同的系统中得出的结果不同。
而把square编写成函数 int square(int x) {return x*x}
那么执行代码后,a的值为4,b的值为9.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值