预处理指令
大多数的预处理指令都属于3中类型之一 宏定义。#define指令定义一个宏,#under指令删除一个宏定义 文件包含。#include 指令导致一个指令文件的内容被包含到程序中。 条件编译。#if、#ifdef、#ifndef、#elif、#else和#endif指令可以根据预处理器可以测试的条件来确定是将一段文本块包含到程序中还是将其排除在程序之外。剩下的#error、#line、#pragma指令是更特殊的指令,较少用到。 指令都以#开始。#符号不需要在一行的行首,只要它在之前只有空白字符就行。在#后是指令名,接着是指令所需要的其他信息。 在指令的符号之间可以插入任意数量的空格或水平制表符。例如,下面的指令是合法的:
# define N 100
指令总是在第一个换行符处结束,除非明确地指明要延续。如果想在下一行延续指令,我们必须在当前行的末尾使用\字符。例如,下面的指令定义了一个宏来表示硬盘的容量,按字节来计算:
#define DISK_CAPACITY(SIDES * \
TRACKS_PER_SIDE * \
SECTORS_PER_TRAACK * \
BYTES_PER_SECTOE)
指令可以出现在程序中的任何地方。但我们通常将#define和#include指令放在文件的开始,其他指令则放在后面 ,甚至可以放在函数定义的中间。 注释可以与指令放在同一行。实际上,在宏定义的后面加一个注释来解释宏的含义是一种比较好的习惯:
#define FREEZING_PT 32.0f
宏定义
简单的宏(C标准中称为对象式宏)的定义有如下格式: [#define指令 (简单的宏)]
#define 标识符 替换列表
替换列表是一系列的预处理记号,宏的替换列表可以包括标识符、关键字、数值常量、字符串字面量、操作符和排列。
不要在宏定义中放置任何额外的符号,否则它们会被作为替换列表的一部分。例如:
#define N = 100
#define N 100;
使用#define来为常量命名有很多的优点: 1.程序会更易读 2.程序会更易于修改 3.可以帮助避免前后不一致或键盘输入错诶 4.可以对C语法做小的修改 5.对类型重命名 6.控制条件编译 顺便提一下,宏定义的替换列表为空是合法的。
带参数的宏
带参数的宏的定义格式如下: [#define指令 (带参数的宏)]
#define 标识符(x1,x2,x3......xn) 替换列表
在宏的名字和左括号之间必须没有空格。如果有空格,预处理器就会认为是在定义一个简单的宏,其中(x1,x2,x3…xn)为替换列表的一部分。
宏参数没有类型检查 当一个函数被调用时,编译器会检查每一个参数来确定它们是否是正确的类型,打。如果不是,要么将参数转换成正确的类型,要么由编译器产生一条出错的消息。预处理器不会检查宏参数的类型,也不会进行类型转换。
宏可能不止一次地计算它的参数 函数对它的参数只会计算一次,而宏可能会计算两次甚至更多次。如果参数有副作用,多次计算参数的值可能会产生不可预知的结果。举一个例子:
n=MAX(i++,j);
下面是这条语句在预处理之后的结果:
n=((i++)>(j)?(i++):j);
如果i大于j,那么i可能会被(错误地)增加两次,同时n可能赋予错误的值。 由于多次计算宏的参数而导致错误可能非常难于发现,因为宏调用和函数调用看起来是一样的。更糟糕的是,这类宏可能在大多数情况下可以正常工作,仅在特定参数有副作用时失效。为了自我保护,最好避免使用带副作用的参数。
带参数的宏不仅适用于模拟函数调用,还经常用作需要重复书写的代码段模式。例如:
#define PRINTF_INT(n) printf("%d\n",n)
一旦定义了PRINTF_INT,预处理器就会将这行PRINTF_INT(i/j)转换成
printf("%d\n",i/j);
对于在一个宏定义中哪里要加圆括号有两条规则要遵守。首先,如果宏的替换列表中有运算符,那么始终要将替换列表放在圆括号中:
#define TWO_PI (2*3.14159)
其次,如果宏有参数,每个参数每次在替换列表中出现时都要放在圆括号中:
#define SCALE(x) ((x)*10)
为了展示为替换列表添加圆括号的重要性,下面据一些例子:
#define TWO_PI 2*3.14159
在预处理时,语句 conversion_factor = 360/TWO_PI 变为
conversion_factor = 360/2*3.14159;
除法会在乘法之前执行,产生的结果并不是期望的结果。
当参数有宏时,仅给替换列表添加圆括号是不够的。参数的每一次出现都要添加圆括号。例如:
#define SCALE(x) (x*10)
在预处理的过程中,语句 j=SCALE(i+1) 变为 j=(i+1*10); 由于乘法的优先级比加法高,这条语句等价于 j = i+10;当然我们希望的是 j=(i+1)*10;
|