“#define”和内联行为是否相同?

时间:2011-07-05 14:46:15

标签: c++ c c-preprocessor inline-functions

我的标题中有一些简短的定义,如下所示:

#define ROUND_DOWN(a,b)   (a)-(a)%(b)

e.g。

ROUND_DOWN(178,32) = 160

但如果我把它传给它:

ROUND_DOWN(160*2, 32);

然后它会像这样编译?

(160*2)-(160*2)%(32), 

这只是处理160 * 2两次......

我想知道内联函数是否以相同的方式运行? e.g。

inline int RoundDown(int a, int b)
{
return (a)-(a)%(b)
}

160 * 2会以“int a”的形式存储为320,然后计算会起作用,还是会与定义相同?

更好的例子是:

RoundDown((x+x2)*zoom, tile_width);

5 个答案:

答案 0 :(得分:8)

“#define”和内联行为相同吗?

不,他们不要!

宏和内联函数之间存在许多差异。

- 没有评价次数

作为内联函数的参数传递的表达式被计算一次。

在某些情况下,作为参数传递给宏的表达式可以多次计算。 每次在宏中使用参数时,都会对该参数进行求值。

代码示例:

#define max(a,b) (a>b?a:b)

int main()
{

  int a = 0;
  int b = 1;

  int c = max(a++, b++);

  cout << a << endl << b << endl;
  return 0;

}

意图可能是打印1和2,但宏扩展为:

int c = a++ > b++ ? a++ : b++;

b增加两次,程序打印1和3.

- 谁评估

内联函数由编译器评估,而宏在预编译时由预编译器进行评估。

- 类型检查

内联函数遵循在正常函数上强制执行的所有类型安全协议。 检查参数类型,并正确执行必要的转换。 在将内联函数放入符号表之前,编译器执行返回类型检查和函数签名 它们可以被重载以对正确类型的数据执行正确的操作。

与内联函数相比,宏更容易出错。参数未键入(宏适用于任何算术类型的对象)。 编译期间没有进行错误检查。

代码示例:

#define MAX(a, b) ((a < b) ? b : a)

int main( void)
{
   cout << "Maximum of 10 and 20 is " << MAX("20", "10") << endl;
   return 0;
}

可以将字符串传递给执行某种整数运算的宏,宏也不会抱怨!

- 建议或命令?

Inline只是对编译器的建议。编译器决定是否内联扩展函数。

宏将永远扩展。

- 调试怎么样?

可以轻松调试内联函数,因为您可以在内联函数定义中设置断点并逐步进入调试方法。

宏不能用于调试,因为它们在预编译时展开。

答案 1 :(得分:5)

首先,您应该假设所有常量表达式都是在编译时进行计算的,因此在运行程序时,乘法永远不会存在。

其次,你不能依赖inline任何影响,它只是对编译器的暗示,而不是一个要求。

但是即使函数没有内联,表达式也不会被计算两次,因为参数传递要求在函数体运行之前对其进行求值。

答案 2 :(得分:3)

#define是简单的文本替换,所以(正如您所注意到的)您可能需要小心括号等。inline参数会被正常解析。

条件有related issue

答案 3 :(得分:2)

名义上,函数参数160*2只被评估一次,然后结果在函数体中使用,而宏评估160*2两次。如果参数表达式有副作用,那么您可以看到[*]:ROUND_DOWN(printf("hi!\n"), 1); vs RoundDown(printf("hi!\n"), 1);

实际上,无论函数是内联还是扩展宏,它只是表达式中的整数运算,没有副作用。优化编译器可以计算整个宏/函数调用的结果,并将答案粘贴在发出的代码中。因此,您可能会发现宏和内联函数导致执行的代码完全相同,因此int a = ROUND_DOWN(160*2, 32);int a = RoundDown(160*2, 32);可能都与int a = 320;相同。

如果没有副作用,优化也可以存储和重复使用中间结果。所以int c = ROUND_DONW(a*2, b);最终可能会发出看起来好像你写过的代码:

int tmp = a*2;
int c = tmp - tmp % b;

请注意,是否实际内联函数是编译器根据自己的优化规则做出的决定。这些规则可能考虑该函数是否标记为inline,但很可能不会,除非您使用编译器选项强制进行内联或其他任何操作。

所以,假设一个体面的编译器没有理由使用宏 - 对于你的宏,特别是你只是想请某人来写并写:

int a = ROUND_DOWN(321, 32) * 2;

然后浪费几分钟,想知道为什么结果是319。

[*]虽然不会被带走 - 对于一些带有副作用的表达式,例如i++其中i是一个整数,由于缺少序列点,宏具有未定义的行为

答案 4 :(得分:1)

在你给出常量的例子中,在任何合理的编译器上,两个版本都将在编译时计算常量。

假设您实际上在询问传入变量的情况,我希望编译器的优化器在两种情况下生成相同的代码(如果保存结果更有效,则不会进行两次乘法运算。最后,内联函数 为编译器提供了进行实际函数调用的选项,如果它可以提高性能的话。

最后请注意,我不会担心像这样的微优化,因为99%它只会对你的程序性能没有影响 - I / O将成为你的瓶颈。