【问题标题】:Which one is better to use in C and C++ programming?在 C 和 C++ 编程中使用哪个更好?
【发布时间】:2017-10-17 20:25:32
【问题描述】:

下面两个代码sn-ps有什么区别吗?哪个更好用?其中一个更快吗?

案例 1:

int f(int x) 
{
    int a;
    if(x)
        a = 42;
    else
        a = 0;
    return a; 
}

案例 2:

int f(int x) 
{
    int a;
    if(x)
        a = 42;
    return a; 
}

【问题讨论】:

  • 检查生成的程序集...看起来一样。 :) 编辑: 可能在第二种情况下添加了额外的初始化。
  • 确保在没有else的情况下初始化a。我会支持第二个可读性偏好,但应该定义更好的性能、可读性和维护的含义。
  • 第一个更好。为什么?因为第二个显然是错误的,因为如果使用 0 参数调用 f,那么 a 不会被初始化并且返回值将是不确定的。
  • 提示:哪一个会产生警告,为什么?
  • int f(int x) { return x?42:0; } 有什么问题?无论如何,应该内联这样一个微小的函数,因为编译器很可能无论如何都会优化整个函数。

标签: c++ c performance if-statement optimization


【解决方案1】:

其实两个sn-ps都可以返回完全不同的结果,所以没有更好的……

案例2中,您可以返回一个未初始化的变量a,这可能会导致非零的垃圾值...

如果你是这个意思:

int f(int x) 
{
    int a = 0;
    if(x)
        a = 42;
    return a; 
}

那我会说更好,因为它更紧凑(但你只保存了一个 else,反正不会浪费多少计算量)

【讨论】:

  • 更紧凑并不总是一件好事。在这种情况下,由于它很简单,我认为是。
【解决方案2】:

问题不是“哪个更好”。问题是“两者都有效吗?”

答案是不,它们不会同时工作。一个是正​​确的,另一个是不可能的。因此,性能甚至不是问题。

以下结果导致a 具有 c99 标准第 3.17.2 和 3.17.3 节中提到的“不确定值”或“未指定值”(可能是后者,尽管我不清楚。 )

int a;
if(x)
    a = 42;
return a; 

这反过来意味着该函数将返回一个未指定的值。这意味着这绝对不能保证您将获得什么价值。

如果你不走运,你可能会得到零,因此继续使用上面那段糟糕的代码,却不知道你以后一定会遇到很多麻烦。

如果你幸运,你会马上得到类似 0x719Ab32d 的东西,所以你会立即知道你搞砸了。

如果你尝试编译它,任何体面的 C 编译器都会给你一个警告,所以你问这个问题的事实意味着你没有启用足够数量的警告。不要尝试在未启用最大可能警告数的情况下编写 C 代码(或任何代码);它永远不会带来任何好处。了解如何在 C 编译器上启用警告,并尽可能多地启用它们。

【讨论】:

  • The following is undefined behavior (Wikipedia):...恕我直言,这是一个错误声明....只要不使用返回值,应该没问题,只是未指定的行为由于不确定的价值....视图?
  • @SouravGhosh 您可能是对的,但不是说应该没问题,更确切地说是不会发生任何高度意外的事件,例如崩溃。 (因此也许没有一碗矮牵牛花,没有什么比一个随机数更奇怪的了。)但是让我们看看其他人怎么说。
  • @SouravGhosh 好的,我修改了我的答案。 (我不得不删除关于抹香鲸和矮牵牛碗的那句话。该死,我喜欢那句话。)
【解决方案3】:

注意:我假设你的第二个 sn-p 中未初始化的 a 是一个类型,它是 int a = 0。

我们可以使用 gdb 来检查差异:

(gdb) list f1
19  {
20      int a;
21      if (x)
22          a = 42;
23      else
24          a = 0;
25      return a;
26  }
(gdb) list f2
28  int f2(int x)
29  {
30      int a = 0;
31      if (x)
32          a = 42;
33      return a;
34  }

现在让我们看看带有-O3的汇编代码:

(gdb) disassemble f1
Dump of assembler code for function f1:
   0x00000000004007a0 <+0>: cmp    $0x1,%edi
   0x00000000004007a3 <+3>: sbb    %eax,%eax
   0x00000000004007a5 <+5>: not    %eax
   0x00000000004007a7 <+7>: and    $0x2a,%eax
   0x00000000004007aa <+10>:    retq   
End of assembler dump.
(gdb) disassemble f2
Dump of assembler code for function f2:
   0x00000000004007b0 <+0>: cmp    $0x1,%edi
   0x00000000004007b3 <+3>: sbb    %eax,%eax
   0x00000000004007b5 <+5>: not    %eax
   0x00000000004007b7 <+7>: and    $0x2a,%eax
   0x00000000004007ba <+10>:    retq   
End of assembler dump.

如您所见,没有区别。让我们用 -O0 禁用优化:

(gdb) disassemble f1
Dump of assembler code for function f1:
   0x00000000004006cd <+0>: push   %rbp
   0x00000000004006ce <+1>: mov    %rsp,%rbp
   0x00000000004006d1 <+4>: mov    %edi,-0x14(%rbp)
   0x00000000004006d4 <+7>: cmpl   $0x0,-0x14(%rbp)
   0x00000000004006d8 <+11>:    je     0x4006e3 <f1+22>
   0x00000000004006da <+13>:    movl   $0x2a,-0x4(%rbp)
   0x00000000004006e1 <+20>:    jmp    0x4006ea <f1+29>
   0x00000000004006e3 <+22>:    movl   $0x0,-0x4(%rbp)
   0x00000000004006ea <+29>:    mov    -0x4(%rbp),%eax
   0x00000000004006ed <+32>:    pop    %rbp
   0x00000000004006ee <+33>:    retq   
End of assembler dump.
(gdb) disassemble f2
Dump of assembler code for function f2:
   0x00000000004006ef <+0>: push   %rbp
   0x00000000004006f0 <+1>: mov    %rsp,%rbp
   0x00000000004006f3 <+4>: mov    %edi,-0x14(%rbp)
   0x00000000004006f6 <+7>: movl   $0x0,-0x4(%rbp)
   0x00000000004006fd <+14>:    cmpl   $0x0,-0x14(%rbp)
   0x0000000000400701 <+18>:    je     0x40070a <f2+27>
   0x0000000000400703 <+20>:    movl   $0x2a,-0x4(%rbp)
   0x000000000040070a <+27>:    mov    -0x4(%rbp),%eax
   0x000000000040070d <+30>:    pop    %rbp
   0x000000000040070e <+31>:    retq   
End of assembler dump.

现在有一个区别,随机参数 x 的第一个版本平均会更快,因为它比第二个少一个 mov。

【讨论】:

    【解决方案4】:

    在任何一种情况下你都不需要额外的空间 - 你可以做这样的事情 -

    int f(int x) 
    {
        if(x)
            return  42;
        else
            return  0;
    }
    

    顺便说一句,在您的第二个函数中,您尚未初始化 a。

    【讨论】:

      【解决方案5】:

      我更喜欢这个(你的第二个 sn-p):

      int f(int x) {
      
          int a = 0;
      
          if (x) {
              a = 42;
          }
          return a;
      }
      
      • 所有东西都应该有大括号。即使现在我在 if 块中只有一行,我稍后也会添加更多。
      • 我不会将大括号放在自己的行上,因为这样会毫无意义地浪费空间。
      • 为了便于阅读,我很少将块与条件放在同一行。

      【讨论】:

      • 这不能回答问题
      • @MichaelWalz。为什么这没有回答这个问题?他想知道哪个代码 sn-p 更好,我告诉了他。我补充说我认为用户括号是必要的。
      • 但是你没有告诉他第二个程序是完全错误的。查看赞成的答案。
      【解决方案6】:

      如果您的第二个代码是

      int f(int x) 
      {
          int a=0;
          if(x)
              a = 42;
          return a; 
      }
      

      而不是

      int f(int x) 
      {
          int a;
          if(x)
              a = 42;
          return a; 
      }
      

      没关系。编译器会将它们转换为相同的优化代码

      【讨论】:

      • 这并不能回答这里的问题。
      • @MichaelWalz 如果问题被编辑并且我是对的怎么办?我们都知道作者试图比较两个应该做同样事情的代码
      • 不,我不知道。也许你能读懂他的心思,但我不能。
      • @MichaelWalz 也有类似的回答,为什么你也没有留下评论?
      • 我确实发表了评论。
      猜你喜欢
      • 2015-02-23
      • 1970-01-01
      • 2016-06-11
      • 2013-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多