在之前的question中,我认为一个好的答案因建议使用宏而被拒绝
#define radian2degree(a) (a * 57.295779513082)
#define degree2radian(a) (a * 0.017453292519)
而不是内联函数。请原谅新手问题,但在这种情况下宏的错误是什么?
答案 0 :(得分:8)
其他大多数答案都讨论了为什么宏是邪恶的,包括你的例子如何有一个共同的宏使用漏洞。这是Stroustrup的观点:http://www.research.att.com/~bs/bs_faq2.html#macro
但你的问题是询问哪些宏仍然有用。有些东西的宏比内联函数要好,而且你正在做的事情就是用内联函数完成的,例如:
assert()
中创建错误消息)offsetof()
使用类型名称创建一个强制转换操作的多少次实现)但是使用具有内联函数的语言,不需要更常见的宏使用。当我处理不支持内联函数的C编译器时,我甚至不愿意使用宏。如果可能的话,我尽量不使用它们来创建类型无关的功能(创建几个带有类型指示符作为名称一部分的函数)。
我也开始使用枚举来命名数字常量而不是#define
。
答案 1 :(得分:7)
对于这个特定的宏,如果我按如下方式使用它:
int x=1;
x = radian2degree(x);
float y=1;
y = radian2degree(y);
没有类型检查,x,y将包含不同的值。
此外,以下代码
float x=1, y=2;
float z = radian2degree(x+y);
不会按照您的想法行事,因为它会转换为
float z = x+y*0.017453292519;
而不是
float z = (x+y)+0.017453292519;
这是预期的结果。
这些只是虚假行为和误用宏可能具有的几个例子。
修改强>
您可以看到有关此here
的其他讨论答案 2 :(得分:6)
有一些关于宏的严重邪恶的东西。
他们是文本处理,并没有作用域。如果您#define foo 1
,则后续使用foo
作为标识符将失败。这可能导致奇怪的编译错误和难以发现的运行时错误。
他们不接受正常意义上的争论。您可以编写一个函数,它将获取两个int
值并返回最大值,因为参数将被评估一次,之后使用的值。你不能写一个宏来做这件事,因为它会评估至少一个参数两次,并且失败时使用max(x++, --y)
之类的东西。
还有一些常见的陷阱。在它们中很难得到多个语句,它们需要很多可能多余的括号。
在您的情况下,您需要括号:
#define radian2degree(a) (a * 57.295779513082)
需要
#define radian2degree(a) ((a) * 57.295779513082)
并且你仍然踩着任何在某个内部范围内编写函数radian2degree
的人,确信该定义在其自己的范围内有效。
答案 3 :(得分:2)
宏经常被滥用,如你的例子所示,人们很容易犯错误。取表达式radian2degree(1 + 1):
更一般地说,宏是邪恶的,因为它们看起来像函数所以它们会像使用函数一样诱骗你使用它们,但它们有自己的微妙规则。在这种情况下,正确的方法是写它(注意a周围的paranthesis):
#define radian2degree(a) ((a) * 57.295779513082)
但你应该坚持使用内联函数。有关邪恶宏及其细微之处的更多示例,请参阅C ++ FAQ Lite中的这些链接:
答案 4 :(得分:1)
如果可能,请始终使用内联函数。这些是类型安全的,不容易重新定义。
定义可以是未定义的redfined,并且没有类型检查。
答案 5 :(得分:1)
编译器的预处理器是一个很好的东西,因此是一个聪明的技巧的可怕候选人。正如其他人所指出的那样,编译器很容易误解你对宏的意图,你很容易误解宏实际会做什么,但最重要的是,你不能在调试器中插入宏! / p>
答案 6 :(得分:0)
宏是邪恶的,因为你可能最终传递的不仅仅是一个变量或一个标量,这可以解决一个不需要的行为(定义一个最大的宏来确定a和b之间的最大值,但是将一个++和b ++传递给宏和看看会发生什么。)
答案 7 :(得分:0)
如果你的函数无论如何都要内联,那么函数和宏之间没有性能差异。但是,函数和宏之间存在一些可用性差异,所有这些都有利于使用函数。
如果正确构建宏,则没有问题。但是如果你使用一个函数,编译器会每次都为你正确地执行它。因此,使用函数会使编写错误代码变得更加困难。