《C陷阱与缺陷》----第六章 预处理器

x33g5p2x  于2022-03-28 转载在 其他  
字(1.9k)|赞(0)|评价(0)|浏览(203)

关键点:宏只是对程序的文本起作用。

6.1 不能忽视宏定义中的空格

观察下面宏定义:

#define f (x) ((x)-1)

f(x)代表

(x) ((x)-1)

而我们像要它代表的是

((x)-1)

原因就是在f和后面的(x)之间多了一个空格!所以,正确的定义方法是:

#define f(x) ((x)-1)

这一规则不适用于宏调用,而只适用于宏定义。所以,在上面完成宏定义之后,f(3)和f (3)求值后都等于2。注意:函数调用也是完全一样的,即假如我们定义了函数ADD,那么ADD(1,2)和ADD (1,2)求值后的结果都是3。

6.2 宏并不是函数

注意:宏定义中各个参数与整个结果表达式都被括号括起来。但仍然存在其它问题,比如副作用的自增和自减运算符,只要出现一次就进行一次自增或者自减,归根结底是因为宏是文本的替换,所以**要保证使用宏的参数没有副作用。**而函数则不是这样的,函数实参中的自增或者自减运算符只会执行一次,所以也要区分函数和宏。

6.3 宏不是语句

假如我们定义一个宏,能够在出错信息中包含文件名和断言失败处的行号,即

assert(x<y);

在x大于y时什么也不做,在其它情况下则会终止程序。

假如我们这样定义:

#define assert(e) if(!e) assert_error(__FILE,__LINE__)

但是当我们遇到下面这种情况时就会出错:

if(x > 0 && y > 0)
	assert(x > y);
else
	assert(y > x);

上面的写法在展开之后就是这个样子的:

if(x > 0 && y > 0)
	if(!(x>y)) assert_error("foo.c",37);
else
	if(!(y>x)) assert_error("foo.c",39);

把上面的代码进行缩排之后是下面这样的:

if (x > 0 && y > 0)
		if (!(x > y)) assert_error("foo.c", 37);
		else
			if (!(y > x)) assert_error("foo.c", 39);

很明显,上面的代码与我们想要表达的意思完全不一样了。

当然,貌似我们可以在宏assert的定义中用大括号把宏整个给括起来,就能避免这样的问题产生:

#define assert(e)\
	{if(!e) assert_error(__FILE,__LINE__);}

然而。这样就会带来新的问题了,我们上面提到的例子展开后就成了:

if(x > 0 && y > 0)
	{ if(!(x<y)) assert_error("foo.c",37);};
else
	{ if(!(y > x)) assert_error("foo.c",39);};

在else之前的分号是一个语法错误。要解决这个问题,一个办法就是对assert的额调用在后面加一个分号,但这样的用法显得有些”怪异“:

y = distence(p,q);
assert(y>0)
x = sqrt(y);

宏assert的正确定义很不直观,这个定义 看起来类似一个表达式,不是类似于一个语句:

#define assert(e)\
	((void)((e)||_assert_error(__FILE__,__LINE__)))

这个定义实际上利用了||运算符对两侧的操作数一次顺序求值的性质。

6.4 宏不是类型定义

宏只是简单的文本替换,但是typedef则是定义新的类型,新类型与int、float等类型具有相同的地位。

练习

  1. 问:请使用宏来实现max的一个版本,其中max的参数都是整数,要求在宏max的定义中这些整型参数只被求值一次。

答:

static int max_temp1,max_temp2;
#define max(p,q) (max_temp1 = (p),max_temp2 = (q),\
	max_temp1>max_temp2? max_temp1:max_temp2)

只要不是嵌套调用max宏,上面的定义都能正常工作:在嵌套调用max宏的情况下,就不能正常工作。

  1. 问:这个表达式(x) ((x) - 1)是否能够称为一个合法的表达式?

答:可能,且由两种情况:

第一种情况:

typedef int x;
(x) ((x)-1);
//等价于下面的表达式
(int) ((int)-1)

第二种情况:

x是一个函数指针,x指向一个函数指针数组种的某个元素

typedef void(*T)(void*);
T x;

相关文章