-2

我正在学习编程,有时我发现使用变量返回会使我的代码更具可读性。

我想知道这些功能是否执行相同的操作并且同样有效。

情况1:

int Foo1()
{
    int x = 5 + 6 + 7;      // Return variable
    return x;
}

int Foo2(int y)
{
    return 5 + 6 + 7;
}

在这种情况下,我认为初始化和求和发生在编译时,所以它们之间没有区别。

案例二:

int Foo1(int y)
{
    int x = y + 6 + 7;      // Return variable
    return x;
}

int Foo2(int y)
{
    return y + 6 + 7;
}

但是,在这种情况下会发生什么?似乎初始化发生在执行时,它必须执行它。

直接返回值是否比初始化变量然后返回它更快?我应该总是尝试直接返回值而不是使用变量返回吗?

4

3 回答 3

2

您可以自己轻松尝试。
您可以从编译器中获取程序集

没有优化:
( gcc -S -O0 -o src.S src.c)

    .file   "so_temp.c"
    .text
    .globl  case1Foo1
    .type   case1Foo1, @function
case1Foo1:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $18, -4(%rbp)
    movl    -4(%rbp), %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   case1Foo1, .-case1Foo1
    .globl  case1Foo2
    .type   case1Foo2, @function
case1Foo2:
.LFB1:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $18, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE1:
    .size   case1Foo2, .-case1Foo2
    .globl  case2Foo1
    .type   case2Foo1, @function
case2Foo1:
.LFB2:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    %edi, -20(%rbp)
    movl    -20(%rbp), %eax
    addl    $13, %eax
    movl    %eax, -4(%rbp)
    movl    -4(%rbp), %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE2:
    .size   case2Foo1, .-case2Foo1
    .globl  case2Foo2
    .type   case2Foo2, @function
case2Foo2:
.LFB3:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    %edi, -4(%rbp)
    movl    -4(%rbp), %eax
    addl    $13, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE3:
    .size   case2Foo2, .-case2Foo2
    .ident  "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
    .section    .note.GNU-stack,"",@progbits

然后您可以看到, foo2 版本的指令比 foo1 版本的函数少一些。

优化转向 O3:
( gcc -S -O3 -o src.S src.c)

    .file   "so_temp.c"
    .text
    .p2align 4,,15
    .globl  case1Foo1
    .type   case1Foo1, @function
case1Foo1:
.LFB0:
    .cfi_startproc
    movl    $18, %eax
    ret
    .cfi_endproc
.LFE0:
    .size   case1Foo1, .-case1Foo1
    .p2align 4,,15
    .globl  case1Foo2
    .type   case1Foo2, @function
case1Foo2:
.LFB5:
    .cfi_startproc
    movl    $18, %eax
    ret
    .cfi_endproc
.LFE5:
    .size   case1Foo2, .-case1Foo2
    .p2align 4,,15
    .globl  case2Foo1
    .type   case2Foo1, @function
case2Foo1:
.LFB2:
    .cfi_startproc
    leal    13(%rdi), %eax
    ret
    .cfi_endproc
.LFE2:
    .size   case2Foo1, .-case2Foo1
    .p2align 4,,15
    .globl  case2Foo2
    .type   case2Foo2, @function
case2Foo2:
.LFB7:
    .cfi_startproc
    leal    13(%rdi), %eax
    ret
    .cfi_endproc
.LFE7:
    .size   case2Foo2, .-case2Foo2
    .ident  "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
    .section    .note.GNU-stack,"",@progbits

两个版本完全相同。

不过我不认为这是你应该优化自己的东西。
在这种情况下,应该首选可读的代码,尤其是代码通常不会在优化关闭的情况下编译。

于 2019-07-29T00:50:07.290 回答
0

案例 2 效率更高,但通常不需要,因为编译器极有可能将案例 1 优化为案例 2。

如果它不损害性能(如本例中),请寻求可读性。

于 2019-07-29T00:36:48.930 回答
0

任何质量至少适中的编译器,即使在优化水平较低(例如 GCC 的-O1)下,也会将它们编译成相同的代码。在大多数情况下,您可以轻松看到的任何正确优化都将由一个好的编译器执行。

C 标准不要求编译器盲目地将代码编译为执行 C 源代码中的确切步骤的指令。它只需要编译器生成具有相同效果的代码。这些效果是根据可观察行为定义的,其中包括程序的输出、与用户的交互以及对易失性对象(稍后将了解的特殊对象)的访问。只要编译器可以在不改变可观察行为的情况下消除中间变量之类的东西,它们就会消除。

于 2019-07-29T00:50:40.657 回答