(C)如果我有一个包含if的函数,如果条件为真,则可以返回某个值,然后返回一个不同的值。是否或多或少有效使用别人或不打扰?
即。 ...
int foo (int a) {
if ((a > 0) && (a < SOME_LIMIT)) {
b = a //maybe b is some global or something
return 0;
} else {
return 1;
}
return 0;
}
或只是
int foo (int a) {
if ((a > 0) && (a < SOME_LIMIT)) {
b = a //maybe b is some global or something
return 0;
}
return 1;
}
假设GCC,编译代码中的第一个实现结果是否与第二个实现结果不同?
我需要在这里尽可能高效,因此可能减少其他分支会很好 - 但风格上我的内部OCD不喜欢看到一个非0或无效的返回作为a中的最后一条指令因为它不太清楚正在发生什么。所以,如果它无论如何都会被淘汰,那么我可以将其他东西留在那里......
答案 0 :(得分:6)
您可以使用-O3 -S
选项运行gcc以生成优化的汇编代码,以便您可以查看(和比较)优化的汇编。我对您的源进行了以下更改以使它们编译。
档案 a.c :
int b;
int foo (int a) {
if ((a > 0) && (a < 5000)) {
b = a;
return 0;
} else {
return 1;
}
return 0;
}
档案 b.c :
int b;
int foo (int a) {
if ((a > 0) && (a < 5000)) {
b = a;
return 0;
}
return 1;
}
使用gcc -O3 -S a.c
编译 a.c 时,会创建 a.s 文件。在我的机器上它看起来像这样:
.file "a.c"
.text
.p2align 4,,15
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
movl 4(%esp), %edx
movl $1, %eax
leal -1(%edx), %ecx
cmpl $4998, %ecx
ja .L2
movl %edx, b
xorb %al, %al
.L2:
rep
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.comm b,4,4
.ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
.section .note.GNU-stack,"",@progbits
使用gcc -O3 -S b.c
编译 b.c 时,会创建 b.s 文件。在我的机器上它看起来像这样:
.file "b.c"
.text
.p2align 4,,15
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
movl 4(%esp), %edx
movl $1, %eax
leal -1(%edx), %ecx
cmpl $4998, %ecx
ja .L2
movl %edx, b
xorb %al, %al
.L2:
rep
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.comm b,4,4
.ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
.section .note.GNU-stack,"",@progbits
请注意,foo:
的汇编实现是相同的。因此,在这种情况下,使用此版本的GCC,编写代码的方式无关紧要。
答案 1 :(得分:1)
检查两个实现之间的目标文件。放置一个汇编标题,例如
PortionInQuestion:
然后,它将作为标签显示在您的程序集文件中,您可以看到生成的程序集是如何不同的。它们可能根本没有不同(因为优化),或者它们可能完全不同。没有看到原始程序集,就没有办法告诉编译器如何优化它。
答案 2 :(得分:1)
您将无法访问第一个示例中的最后一个return 0;
。我会说你的第二个在风格上至少是因为这个更清楚。同一件事的代码通常是一件好事。
关于性能,你可以查看程序集可执行文件,如果你喜欢这样的东西或分析代码,看看是否有实际的差异。我的赌注无关紧要。
最后,如果您的编译器支持优化标志,请使用它们!
答案 3 :(得分:1)
当然这取决于编译器。我想市场上的每个(不错的)编译器在这两种情况下都会产生完全相同的输出。然而,任何关于优化的书都不鼓励尝试这种微观优化!
选择更易读的表格。
答案 4 :(得分:1)
我会这样写...
int foo (int a) {
if ((a > 0) && (a < SOME_LIMIT)) {
b = a //maybe b is some global or something
return 0;
} else {
return 1;
}
}
整个函数只是一个布尔值,用于调整b的值大于零且小于某个常量。并且if语句决定了回报。无需为函数添加默认返回。默认返回将使if条件无效。
答案 5 :(得分:1)
int foo (int a) {
/* Nothing to do: get out of here */
if (a <= 0 || a >= SOME_LIMIT) return 1;
b = a; // maybe b is some global or something
return 0;
}
在效率方面几乎没有任何差异(最昂贵的部分是函数调用加上返回)。
对于人类读者来说,最简单的“缩进”代码(如上所述)是最容易阅读和理解的。
顺便说一句
生成的汇编程序对我来说也很简单,完全等同于if (a >0 && a < LIMIT)
表单。
.file "return.c"
.text
.p2align 4,,15
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
leal -1(%rdi), %edx
movl $1, %eax
cmpl $2998, %edx
ja .L2
movl %edi, b(%rip)
xorb %al, %al
.L2:
rep
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.globl b
.bss
.align 16
.type b, @object
.size b, 4
b:
.zero 4
.ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
.section .note.GNU-stack,"",@progbits