14

在上一个问题中,我认为好的答案被否决了,因为建议使用宏

#define radian2degree(a) (a * 57.295779513082)
#define degree2radian(a) (a * 0.017453292519)

而不是内联函数。请原谅新手的问题,但是在这种情况下,宏有什么邪恶之处?

4

8 回答 8

8

大多数其他答案都讨论了为什么宏是邪恶的,包括您的示例如何具有常见的宏使用缺陷。这是 Stroustrup 的看法:http ://www.research.att.com/~bs/bs_faq2.html#macro

但是您的问题是询问宏仍然适用于什么。有些事情宏比内联函数更好,这就是你正在做的事情是内联函数无法完成的,例如:

  • 令牌粘贴
  • 处理行号等(例如在 中创建错误消息assert()
  • 处理不是表达式的东西(例如有多少offsetof()使用使用类型名称来创建强制转换操作的实现)
  • 获取数组元素计数的宏(不能用函数来完成,因为数组名称太容易衰减为指针)
  • 在模板不可用的 C 中创建类似于“类型多态”函数的东西

但是对于具有内联函数的语言,宏的更常见用途应该是不必要的。在处理不支持内联函数的 C 编译器时,我什至不愿意使用宏。如果可能的话,我尽量不使用它们来创建与类型无关的函数(创建几个函数,并将类型指示符作为名称的一部分)。

我也开始使用枚举来命名数字常量,而不是#define.

于 2009-10-29T00:07:10.830 回答
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;

这是预期的结果。

这些只是错误行为和滥用宏可能具有的几个示例。

编辑

您可以在此处查看有关此的其他讨论

于 2009-10-28T21:55:16.160 回答
7

关于宏有一些严格意义上的邪恶之处。

它们是文本处理,并且没有范围。如果 you #define foo 1,那么任何后续使用fooas 标识符都将失败。这可能会导致奇怪的编译错误和难以发现的运行时错误。

他们不接受通常意义上的争论。您可以编写一个接受两个int值并返回最大值的函数,因为参数将被评估一次,然后使用这些值。您不能编写宏来执行此操作,因为它至少会评估一个参数两次,并且会以类似max(x++, --y).

还有常见的陷阱。很难在其中获得多个语句,并且它们需要很多可能是多​​余的括号。

在您的情况下,您需要括号:

#define radian2degree(a) (a * 57.295779513082)

需要是

#define radian2degree(a) ((a) * 57.295779513082)

而且您仍然踩着radian2degree在某个内部范围内编写函数的任何人,并确信该定义将在其自己的范围内起作用。

于 2009-10-28T22:07:45.887 回答
2

如果可能,请始终使用内联函数。这些是类型安全的,不能轻易重新定义。

定义可以重新定义为未定义,并且没有类型检查。

于 2009-10-28T21:54:41.483 回答
2

宏相对经常被滥用,使用它们很容易出错,如您的示例所示。取表达式radian2degree(1 + 1):

  • 使用宏,它将扩展到 1 + 1 * 57.29... = 58.29...
  • 有了一个函数,它就会是你想要的,即 (1 + 1) * 57.29... = ...

更一般地说,宏是邪恶的,因为它们看起来像函数,所以它们会欺骗你像函数一样使用它们,但它们有自己的微妙规则。在这种情况下,正确的写法是(注意 a 周围的括号):

#define radian2degree(a) ((a) * 57.295779513082)

但是你应该坚持使用内联函数。有关邪恶宏及其微妙之处的更多示例,请参阅 C++ FAQ Lite 中的这些链接:

于 2009-10-28T22:05:10.797 回答
1

编译器的预处理器是一个很麻烦的东西,因此是聪明的技巧的可怕候选者。正如其他人所指出的,编译器很容易误解你对宏的意图,你也很容易误解宏实际上会做什么,但最重要的是,你不能在调试器中单步执行宏!

于 2009-10-28T22:06:27.970 回答
0

宏是邪恶的,因为您最终可能传递的不仅仅是一个变量或一个标量,这可能会导致不需要的行为(定义一个 max 宏来确定 a 和 b 之间的最大值,但将 a++ 和 b++ 传递给宏,看看会发生什么)。

于 2009-10-28T21:57:08.687 回答
0

如果您的函数无论如何都将被内联,那么函数和宏之间没有性能差异。但是,函数和宏之间存在一些可用性差异,所有这些差异都支持使用函数。

如果您正确构建宏,则没有问题。但是如果你使用一个函数,编译器每次都会为你正确地执行它。因此,使用函数会使编写糟糕的代码变得更加困难。

于 2009-10-28T21:58:16.063 回答