函数在`if`的分支中返回与`if`之外,在C++中
Posted
技术标签:
【中文标题】函数在`if`的分支中返回与`if`之外,在C++中【英文标题】:Function return in branches of an `if` vs outside the `if`, in C++ 【发布时间】:2016-05-06 10:07:16 【问题描述】:编译后的代码有区别吗
double func(bool x)
if (x)
return 1.0;
else
return 2.0;
;
和
double func(bool x)
if (x)
return 1.0;
;
return 2.0;
是否有性能方面的理由偏爱其中一个?
我唯一知道的是第二个打字速度更快。
我一直在使用 g++,但如果有不同的话,了解其他人的情况会很有趣。
【问题讨论】:
为了提高可读性,删除
之后的;
。
你可以编译它并查看程序集以找出答案。
@NathanOliver 我不知道它是如何工作的。容易吗?
@myfirsttime1 使用gcc.godbolt.org 就像将代码复制到编辑器中一样简单。 example
@SergeyA 您不必理解它就可以看到它与 OP 想知道的相同。
【参考方案1】:
“两者之间的编译代码是否存在差异”无论如何都是一个糟糕的开始。 C++ 标准未指定汇编输出,但它必须反映标准中声明的规则。每条指令之间有或没有一百万个无操作 - 在大多数情况下,它同样符合要求。因此,使用标准作为参考,您不能对实际的程序集输出说任何话,因为它取决于所使用的实际编译器。 对于相关问题,您需要指定编译器和版本、操作系统、架构等。否则,您只需自己编译和比较程序集输出即可。
无论如何,为了解决您的实际问题,输出在一个体面的编译器上应该不会有所不同。
在我的带有 GCC 5.2.0 的 x86-64 机器上:
第一:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 89 f8 mov %edi,%eax
6: 88 45 fc mov %al,-0x4(%rbp)
9: 80 7d fc 00 cmpb $0x0,-0x4(%rbp)
d: 74 0a je 19 <_Z5func0b+0x19>
f: f2 0f 10 05 00 00 00 movsd 0x0(%rip),%xmm0
16: 00
17: eb 08 jmp 21 <_Z5func0b+0x21>
19: f2 0f 10 05 00 00 00 movsd 0x0(%rip),%xmm0
20: 00
21: 5d pop %rbp
22: c3 retq
第二:
23: 55 push %rbp
24: 48 89 e5 mov %rsp,%rbp
27: 89 f8 mov %edi,%eax
29: 88 45 fc mov %al,-0x4(%rbp)
2c: 80 7d fc 00 cmpb $0x0,-0x4(%rbp)
30: 74 0a je 3c <_Z5func1b+0x19>
32: f2 0f 10 05 00 00 00 movsd 0x0(%rip),%xmm0
39: 00
3a: eb 08 jmp 44 <_Z5func1b+0x21>
3c: f2 0f 10 05 00 00 00 movsd 0x0(%rip),%xmm0
43: 00
44: 5d pop %rbp
45: c3 retq
没有区别。
【讨论】:
奇怪的答案。它假定,没有理由编写高效的代码,因为编译器“可以包含一百万个 noops”。 @SergeyA 重写。更好?【参考方案2】:这里没有性能差异,因为一旦编译器采用“then”分支,“else”分支就变得无法访问。优化编译器生成的代码应该是相同的。
您的示例的可读性也没有改变。但是,当else
分支很长而“then”分支很短时,第二个 sn-p 读取效果更好,因为它的嵌套级别较低。
“then”分支很短而“else”分支很长的情况很常见 - 当您执行参数检查并抛出异常或提供提前终止以防参数超出范围时会发生这种情况。当您在函数顶部考虑多个验证时,第二种方法的优势变得更加明显,例如
if (!arg1.isInRange())
return FAILURE_REASON_1;
else
if (!arg2.isInRange())
return FAILURE_REASON_2;
else
if (!arg3.isInRange())
return FAILURE_REASON_3;
else
... // Payload code goes here
对比
if (!arg1.isInRange())
return FAILURE_REASON_1;
if (!arg2.isInRange())
return FAILURE_REASON_2;
if (!arg3.isInRange())
return FAILURE_REASON_3;
... // Payload code goes here
【讨论】:
是的。缩进是唯一需要考虑的因素,而且很重要。 要考虑的另一件事是,如果在 if-else-if-else-if -else 情况下有很多分支,通过将返回值放在每个块中,如果不小心会出现编译器错误忘记一条路。如果您在底部有一揽子退货,则不会出现该错误。【参考方案3】:对于初学者来说,不需要包含一个空语句
double func(bool x)
if (x)
return 1.0;
else
return 2.0;
;
^^^
至于问题那么编译器可以为两个函数生成相同的目标代码。
但对于我来说,我更喜欢以下功能:)
double func( bool x ) return x ? 1.0 : 2.0;
我不排除编译器可以生成与您帖子中前两个函数相同的目标代码。
而且它本身可以使函数内联。
因此,与这些函数相关的唯一问题是哪一个更具可读性。
至于您展示的函数,如果函数中没有其他语句,则第一个与第二个相比更具可读性。阅读第二个函数的代码,我会花一些时间自己回答为什么没有使用else
.:)
【讨论】:
以上是关于函数在`if`的分支中返回与`if`之外,在C++中的主要内容,如果未能解决你的问题,请参考以下文章