C-可直接返回或返回的变量



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

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

案例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;
}

但是,在这种情况下会发生什么?看来初始化发生在执行时间,并且必须执行。

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

您可以轻松地自己尝试。
您可以从编译器获得组件

没有优化:
(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

两个版本都是完全相同的。

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

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

如果不损害性能(如在这种情况下(。

任何至少适度质量的编译器都将在优化级别(例如GCC的-O1(中,将其编译为相同的代码。在大多数情况下,您可以轻松看到的任何正确优化将由一个好的编译器执行。

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

最新更新