4

如果你有带常量的函数调用,它没有副作用,也不依赖于任何东西,如下所示:

int foo(int a,int b)
{         
  返回 a+b;   
}

函数是否内联?或者,也许函数是在编译时评估的,这个评估的结果插入到函数调用的位置?

4

7 回答 7

7

我尝试使用相当旧的 gcc 编译它 -

#include <iostream>

int foo(int a,int b)
{
    return a+b;
} 


int main()
{
    std::cout << foo(100, 123) ;
}

主要编译到这个 -

LFB1439:
    subq    $8, %rsp
.LCFI1:
    movl    $223, %esi
    movl    $_ZSt4cout, %edi
    call    _ZNSolsEi
    xorl    %eax, %eax
    addq    $8, %rsp
    ret

所以它在编译时编译了加法,得到了 223。

显然,结果取决于您的代码和编译器,但这表明它可以并且在编译时内联并计算加法(如果可以的话)。

于 2012-09-07T11:33:22.980 回答
4

不在 C++ 中。它们不会像那样在编译时执行——除非编译器神奇地做到了。但是,这不能强求。

但是,对于 C++11,您可以使用constexpr它来确保在编译时对其进行评估,例如:

constexpr int get_five() {return 5;}

因此,您可以将函数重写为:

constexpr int foo(int a,int b)
{         
  return a+b;   
}

请注意,如果此函数的参数并不总是恒定的,您不必担心。

来自维基百科:

如果使用不是常量表达式的参数调用 constexpr 函数或构造函数,则调用的行为就好像该函数不是 constexpr,并且结果值不是常量表达式。同样,如果 constexpr 函数的 return 语句中的表达式对于特定调用未计算为常量表达式,则结果不是常量表达式。

这意味着这foo(1,1)将是恒定的,但是:

int i,j;
cin >> i >> j;
foo(i,j) // this is not constant

参考:http ://en.wikipedia.org/wiki/C%2B%2B11#constexpr_-_Generalized_constant_expressions

于 2012-09-07T11:31:02.807 回答
2

如果您在头文件中定义它,那么它很有可能被内联。如果您使用完整的编译时常量作为其参数,那么编译器应该能够在编译时执行该函数。

即使没有这样的保证,你也应该相信你的编译器。他们非常擅长优化您的代码。如果要确保函数在编译时执行,可以添加constexpr重载(仅限 C++11):

constexpr int foo(int a,int b){
    return a+b;
}

我尝试了以下代码段:

int add(int a, int b) {
    return a + b;
}

int main() {
    return add(5, 2);
}

当使用 GCC 和 -O3 标志编译时,它被编译为:

0x08048300 <+0>:    mov    $0x7,%eax
0x08048305 <+5>:    ret

因此,您可以看到它实际上是在编译时执行的。

于 2012-09-07T11:28:36.723 回答
1

是否执行此类优化不是 C 和 C++ 语言的定义部分。从本质上讲,只要生成的代码根据源代码是有效的,编译器就可以自由地进行优化。在一般情况下,在更高的优化级别,这个调用可以是内联的,或者如果调用站点总是传入常量(在编译时已知的值),则可以在编译时计算结果并且完全避免任何运行时开销。

优化编译器选择不内联函数的一般情况是:

  • 递归函数
  • 当它更倾向于大小而不是速度时,或者如果内联会极大地扭曲最终二进制文件的大小

另一个需要注意的问题是内联会改变函数的链接。

在 GCC 和 G++ 上编译以下 C 代码-O3

int foo(int a, int b) {
    return a+b;
}

int main(void)
{
    return foo(1, 2);
}

产生以下汇编代码:

00000000004004e0 <main>:
main():
  4004e0:       b8 03 00 00 00          mov    $0x3,%eax
  4004e5:       c3                      retq
于 2012-09-07T11:29:27.290 回答
0

您可以检查程序集列表以查看它是否被内联,但如前所述,它是特定于编译器的。

于 2012-09-07T11:31:02.813 回答
0

这取决于编译器和优化设置,但通常你可以假设任何足够高级的编译器都会在你开启至少一点优化时内联这样一个微不足道的函数。

当你想确保函数是内联的,你总是可以用 inline 关键字声明它:

inline int foo(int a,int b){
    return a+b;
} 

但是通常应该避免这种善意的提示,因为大多数编译器比大多数程序员更善于决定内联哪些函数。

于 2012-09-07T11:31:50.643 回答
0

如何在编译时计算此函数的可能场景:
1)foo在内联优化阶段之一期间编译器内联函数。
2) 在常量传播 优化阶段,编译器可以“传播”编译时已知的变量值,即常量和常量表达式。

注意:在您看到程序的汇编代码之前,您永远不会确切地知道该函数是否被内联。即使您使用inline说明符。编译器可能会忽略此说明符或没有此说明符的内联函数。

于 2012-09-07T12:16:14.000 回答