C++ C/C++;

C++ C/C++;,c++,c,optimization,compiler-construction,C++,C,Optimization,Compiler Construction,如果使用常量进行函数调用,则不会产生任何副作用,并且不依赖于任何内容,如以下内容: int foo(int a,int b) { return a+b; } int foo(int a,int b) { 返回a+b; } 函数是否内联?或者,可能是在编译时对函数求值,并将求值结果插入函数调用的位置?如果在头文件中定义,则很有可能将其内联。如果使用整型编译时常量作为参数,那么编译器应该能够在编译时执行该函数 即使没有这样的保证,您也应该信任您的编译器。

如果使用常量进行函数调用,则不会产生任何副作用,并且不依赖于任何内容,如以下内容:

int foo(int a,int b) { return a+b; } int foo(int a,int b) { 返回a+b; }
函数是否内联?或者,可能是在编译时对函数求值,并将求值结果插入函数调用的位置?

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

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

我尝试了以下代码片段:

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
0x08048300:mov$0x7,%eax
0x08048305:ret

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

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

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

  • 递归函数
  • 当它倾向于使用大小而不是速度,或者内联是否会极大地扭曲结束二进制文件的大小
还有一个问题需要注意,内联将改变函数的链接

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

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
0000000000 4004e0:
main():
4004e0:b8 03 00 mov$0x3,%eax
4004e5:c3 retq

<代码> > p>您可以检查程序集列表,看看它是否被内联,但是如前所述,它是编译器专用的。

不在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

参考资料:

这取决于编译器和优化设置,但一般来说,您可以假设,当您至少打开一点优化时,任何足够高级的编译器都会内联这样一个简单的函数

如果要确保函数是内联的,则始终可以使用内联关键字声明它:

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

但是,通常应该避免此类意图良好的提示,因为大多数编译器比大多数程序员更擅长决定内联哪些函数。

我尝试使用相当旧的gcc编译此函数-

#include <iostream>

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


int main()
{
    std::cout << foo(100, 123) ;
}
因此,它在编译时编译了加法,得到223


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

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


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

但如果在编译时无法对其求值或有副作用时使用constexpr会发生什么情况?@coredump则不要使用
constexpr
@coredump,则不能将其用作constexpr,编译器会告诉您。在上面的例子中,
constexpr int x=foo(5,6)将起作用,
constexpr int foo(runtimeVariable1,runtimeVariable2)
不会。@coredump,编译器将假定它可以在编译时进行计算(尽管当它发现它无法计算时可能会抛出错误)。只有当您知道该函数没有副作用,并且除了从其参数中获取输入数据外,在任何地方都不能获取输入数据时,才使用
constexpr
。@juanchopanza,
constexpr int foo(runtimeVariable1,runtimeVariable2)仍将正确编译。它只是不会在编译时执行。编译器将正确地处理常量表达式函数的任何非常量参数。那么constexpr中是否有任何用途?任何此类优化都严重依赖于编译器和函数的复杂性
constexpr
用于在函数太复杂或编译器太幼稚时确保编译器执行您想要的操作。您使用了哪些优化标志?@coredump-
constexpr
告诉编译器它应用的东西可以在需要编译的上下文中使用
#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