预处理详解(一)

1 预定义符号

        __FILE__    //进行编译的源文件
        __LINE__   //文件当前的行号
        __DATE__   //文件被编译的日期
        __TIME__   //文件被编译的时间
        __STDC__   //如果编译器遵循ANSI C,其值为1,否则未定义

        这些预定义符号都是语言内置的。

2 #define

        2.1 #define 定义标识符

#define MAX 1000
#define reg register      //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;)   //用更形象的符号来替换一种实现
#define CASE break;case     //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
             date:%s\ttime:%s\n" ,\
             __FILE__,__LINE__ ,    \
             __DATE__,__TIME__ ) 

        注意我们定义宏后面不能加 “ ;”

2.2 #define 定义宏

        #define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。

        下面是宏的声明方式:

        #define name( parament-list ) stuff
        其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。

注意:

参数列表的左括号必须与name紧邻。
如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

如:

#define SQUARE( x ) x * x

这个宏接收一个参数 x .
如果在上述声明之后,你把

SQUARE( 5 );

置于程序中,预处理器就会用下面这个表达式替换上面的表达式:

5 * 5

        警告:
        这个宏存在一个问题:
        观察下面的代码段:

int a = 5;
printf("%d\n" ,SQUARE( a + 1) );

        乍一看,你可能觉得这段代码将打印36这个值。
        事实上,它将打印11.

        为什么?

        这样就比较清晰了,由替换产生的表达式并没有按照预想的次序进行求值。
        在宏定义上加上两个括号,这个问题便轻松的解决了:

#define SQUARE(x) (x) * (x)

        这样预处理之后就产生了预期的效果: 

printf ("%d\n",(a + 1) * (a + 1) );

        这里还有一个宏定义:

#define DOUBLE(x) (x) + (x)

        定义中我们使用了括号,想避免之前的问题,但是这个宏可能会出现新的错误。

int a = 5;
printf("%d\n" ,10 * DOUBLE(a));

        这将打印什么值呢?

        warning:
        看上去,好像打印100,但事实上打印的是55.
        我们发现替换之后:

printf ("%d\n",10 * (5) + (5));

        乘法运算先于宏定义的加法,所以出现了

        55 .

        这个问题,的解决办法是在宏定义表达式两边加上一对括号就可以了。

        #define DOUBLE( x)     ( ( x ) + ( x ) )

提示:
        所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。

        2.3 #define 替换规则

        在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
        1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
        2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
        3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

注意:
1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

        2.4 #和##

        如何把参数插入到字符串中?

        首先我们看看这样的代码:

char* p = "hello ""bit\n";
printf("hello"" bit\n");
printf("%s", p);

        这里输出的是不是
        hello bit ?
        答案是确定的:是。
        我们发现字符串是有自动连接的特点的。

        1. 那我们是不是可以写这样的代码?:

#define PRINT(FORMAT, VALUE)\
printf("the value is "FORMAT"\n", VALUE);
...
PRINT("%d", 10);

        这里只有当字符串作为宏参数的时候才可以把字符串放在字符串中。
        1. 另外一个技巧是:
        使用 # ,把一个宏参数变成对应的字符串。
        比如:

int i = 10;
#define PRINT(FORMAT, VALUE)\
printf("the value of " #VALUE "is "FORMAT "\n", VALUE);
...
PRINT("%d", i+3);//产生了什么效果?

        代码中的 #VALUE 会预处理器处理为:
        "VALUE" .
        最终的输出的结果应该是:

the value of i+3 is 13

        ## 的作用
##可以把位于它两边的符号合成一个符号。
它允许宏定义从分离的文本片段创建标识符。

#define ADD_TO_SUM(num, value) \
sum##num += value;
...
ADD_TO_SUM(5, 10);//作用是:给sum5增加10.

注:
这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。

 

2.5 带副作用的宏参数

        当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。

        例如:

        MAX宏可以证明具有副作用的参数所引起的问题。

#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);//输出的结果是什么?

        这里我们得知道预处理器处理之后的结果是什么:

z = ( (x++) > (y++) ? (x++) : (y++));

        所以输出的结果是:

x=6 y=10 z=9
     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值