什么时候应该使用宏而不是内联函数?

时间:2009-10-28 21:48:25

标签: c macros inline

在之前的question中,我认为一个好的答案因建议使用宏而被拒绝

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

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

8 个答案:

答案 0 :(得分:8)

其他大多数答案都讨论了为什么宏是邪恶的,包括你的例子如何有一个共同的宏使用漏洞。这是Stroustrup的观点:http://www.research.att.com/~bs/bs_faq2.html#macro

但你的问题是询问哪些宏仍然有用。有些东西的宏比内联函数要好,而且你正在做的事情就是用内联函数完成的,例如:

  • 令牌粘贴
  • 处理行号等(如在assert()中创建错误消息)
  • 处理不是表达式的事物(例如offsetof()使用类型名称创建一个强制转换操作的多少次实现)
  • 宏来获取数组元素的数量(不能用函数来做,因为数组名很容易衰减到指针)
  • 在C中创建'类型多态'类似函数的东西,其中模板不可用

但是使用具有内联函数的语言,不需要更常见的宏使用。当我处理不支持内联函数的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):

  • 使用宏它将扩展为1 + 1 * 57.29 ... = 58.29 ...
  • 有一个功能它将是你想要的,即(1 + 1)* 57.29 ... = ...

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

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

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

答案 4 :(得分:1)

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

定义可以是未定义的redfined,并且没有类型检查。

答案 5 :(得分:1)

编译器的预处理器是一个很好的东西,因此是一个聪明的技巧的可怕候选人。正如其他人所指出的那样,编译器很容易误解你对宏的意图,你很容易误解宏实际会做什么,但最重要的是,你不能在调试器中插入宏! / p>

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

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