3

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

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

例如

ROUND_DOWN(178,32) = 160

但是,如果我将其传递给它:

ROUND_DOWN(160*2, 32);

那么它会像这样编译吗?

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

这只是更多的处理,因为它两次 160*2..

我想知道内联函数的行为是否相同?例如

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

160*2 是否会作为 320 存储在“int a”中,然后计算会起作用,或者它的行为是否与定义相同?

一个更好的例子是调用:

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

5 回答 5

8

“#define”和 inline 的行为是否相同?

不,他们没有!

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

- 评估次数

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

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

代码示例:

#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;
}

可以将字符串传递给执行一些整数运算的宏,而宏不会抱怨!

- 建议还是命令?

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

宏将始终被扩展。

- 调试怎么样?

内联函数可以很容易地调试,因为您可以在内联函数定义处放置一个断点,并逐步进入调试方法。

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

于 2011-07-05T15:20:59.833 回答
5

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

其次,您根本不能依赖inline任何效果,这只是对编译器的提示,而不是要求。

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

于 2011-07-05T14:49:44.197 回答
3

#defines 是简单的文本替换,因此(如您所见)您可能需要小心括号等。 inline参数被正常解析。

关于条件有一个相关的问题

于 2011-07-05T14:55:48.133 回答
2

名义上,函数参数160*2只计算一次,然后在函数体中使用结果,而宏计算160*2两次。如果参数表达式有副作用,那么你可以看到这个[*]:ROUND_DOWN(printf("hi!\n"), 1);vsRoundDown(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++wherei是一个整数,由于缺少序列点,宏具有未定义的行为。

于 2011-07-05T14:51:58.163 回答
1

好吧,在您给出的带有常量的示例中,在任何合理的编译器上,两个版本都会在编译时计算常量。

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

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

于 2011-07-05T14:59:25.780 回答