变量返回还是直接返回?

时间:2019-07-29 00:32:59

标签: c function return

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

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

案例1:

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

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

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

案例2:

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

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

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

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

3 个答案:

答案 0 :(得分:3)

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

未经优化:
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

两个版本完全相同。

我仍然不认为这是您应该优化自己的事情。
在这种情况下,应首选可读性强的代码,尤其是通常在不关闭优化功能的情况下编译代码。

答案 1 :(得分:1)

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

如果不影响性能(在这种情况下),请提高可读性。

答案 2 :(得分:1)

任何质量至少中等的编译器,即使在较低的优化水平下(例如GCC的-O1),也将compile these to the same code。在大多数情况下,您可以轻松看到的任何正确优化都将由一个好的编译器执行。

C标准不需要编译器将代码无意识地编译为执行C源代码中确切步骤的指令。它仅要求编译器产生具有相同效果的代码。这些效果是根据可观察到的行为来定义的,其中包括程序的输出,与用户的交互以及对易失性对象(以后将要学习的特殊对象)的访问。编译器将消除中间变量之类的东西,只要它们可以这样做而不会改变可观察的行为。