prev up next   top/contents search

comp.lang.c FAQ 列表· 第 10.1 题

Q我正在尝试定义一些简单的类似函数的宏,例如

	#define square(x) x * x
但它们并不总是有效。


A定义类似函数的宏时,有三个重要的规则需要记住

  1. 宏展开必须始终用括号括起来,以保护任何低优先级的运算符免受周围表达式的影响。给定(不正确的)square()宏,调用
    	1 / square(n)
    
    将展开为
    	1 / n * n
    
    (其计算结果为(1 / n) * n),而你想要的是
    	1 / (n * n)
    
    (在这种情况下,问题是结合律而非优先级,但效果是相同的。)
  2. 在宏定义中,参数的所有出现都必须用括号括起来,以保护实际参数中的任何低优先级运算符免受宏展开其余部分的影响。再次给定square()宏,调用
    	square(n + 1)
    
    将展开为
    	n + 1 * n + 1
    
    但你想要的是
    	(n + 1) * (n + 1)
    
  3. 如果一个参数在展开中出现多次,如果实际参数是一个具有副作用的表达式,宏可能无法正常工作。再次给定square()宏,调用
    	square(i++)
    
    将展开为
    	i++ * i++
    
    这是未定义的(参见问题 3.2)。

正确定义的square宏,为了符合上述规则 1 和 2,是

	#define square(x) ((x) * (x))
遵循规则 3 更难。有时,通过仔细利用的短路行为&&, ||?:运算符(参见问题 3.6)可以安排出现多次的参数保证只被求值一次。有时,宏只是被记录为不安全,调用者必须记住不要在具有副作用的参数上使用它。其他时候,可能建议不要编写一个类似函数的宏,如果它不能被制成安全的。

(作为一种风格惯例,宏通常定义为大写或全大写名称,以便清楚地表明它们是宏。如果一个类似函数的宏真正模拟了一个函数,那么用全小写名称定义它是可以接受的,但前提是它符合上述所有三个规则。由于我们一直在讨论的平方宏不符合,所以应该将其定义为类似

	#define Square(x) ((x) * (x))	/* UNSAFE */
如果它要被使用的话。)

参考文献:K&R1 第 4.11 节,第 87 页
K&R2 第 4.11.2 节,第 90 页
H&S 第 3.3.6、3.3.7 节,第 49-50 页
CT&P 第 6.2 节,第 78-80 页


prev up next   contents search
关于此 FAQ 列表   关于 Eskimo   搜索   反馈   版权

Eskimo North 托管