关键点:宏只是对程序的文本起作用。
观察下面宏定义:
#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。
注意:宏定义中各个参数与整个结果表达式都被括号括起来。但仍然存在其它问题,比如副作用的自增和自减运算符,只要出现一次就进行一次自增或者自减,归根结底是因为宏是文本的替换,所以**要保证使用宏的参数没有副作用。**而函数则不是这样的,函数实参中的自增或者自减运算符只会执行一次,所以也要区分函数和宏。
假如我们定义一个宏,能够在出错信息中包含文件名和断言失败处的行号,即
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__)))
这个定义实际上利用了||运算符对两侧的操作数一次顺序求值的性质。
宏只是简单的文本替换,但是typedef则是定义新的类型,新类型与int、float等类型具有相同的地位。
答:
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宏的情况下,就不能正常工作。
(x) ((x) - 1)
是否能够称为一个合法的表达式?答:可能,且由两种情况:
第一种情况:
typedef int x;
(x) ((x)-1);
//等价于下面的表达式
(int) ((int)-1)
第二种情况:
x是一个函数指针,x指向一个函数指针数组种的某个元素
typedef void(*T)(void*);
T x;
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/m0_57304511/article/details/123758784
内容来源于网络,如有侵权,请联系作者删除!