代码之家  ›  专栏  ›  技术社区  ›  coredump

C/C中使用常量优化的函数调用++

  •  4
  • coredump  · 技术社区  · 12 年前

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

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

    函数是否内联?或者,也许是在编译时对函数求值,并将求值结果插入函数调用的位置?

    7 回复  |  直到 12 年前
        1
  •  7
  •   jcoder    12 年前

    我试着用一个相当旧的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。

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

        2
  •  4
  •   ronalchn Damien    12 年前

    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

        3
  •  2
  •   mfontanini    12 年前

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

    即使没有这样的保证,你也应该相信你的编译器。他们非常擅长优化您的代码。如果要确保函数在编译时执行,可以添加 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
    

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

        4
  •  1
  •   Mike Kwan Kyr    12 年前

    是否执行这样的优化不是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
    
        5
  •  0
  •   Minion91    12 年前

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

        6
  •  0
  •   Philipp    12 年前

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

    当您希望确保函数是内联的时,您可以始终使用inline关键字声明它:

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

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

        7
  •  0
  •   ThomasMore    12 年前

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

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