当在 c 的 if 语句的测试部分中调用函数时,它的计算结果是否与您正常调用它完全一样? 例如,除了返回值之外的所有影响都会评估并持久化吗?
例如,如果我想在调用 fseek 时包含错误检查,我可以写
if( fseek(file, 0, SEEK_END) ) {fprintf(stderr, "File too long")};
并且在功能上与以下方面相同:
long int i = fseek(file, 0, SEEK_END);
if( i ) {fprintf(stderr, "File too long")};
?
https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html#The-if-Statement
https://www.gnu.org/software/libc/manual/html_node/File-Positioning.html
是的,这是完全相同的。唯一的区别是您将无法再次使用 if 语句中执行的操作的结果。
在这两种情况下,操作都是在条件(比较(发生之前执行的。为了说明这一点,我们可以看到机器代码中两种不同情况的结果是什么。请注意,输出机器代码将根据操作系统和编译器而有所不同。
源文件"a.c":
#include <stdio.h>
int
main(void)
{
FILE *f = fopen("testfile", "r");
long int i = fseek(f, 0, SEEK_END);
if (i)
fprintf(stderr, "Errorn");
return 0;
}
$ gcc -O1 a.c -o a
源文件"b.c":
#include <stdio.h>
int
main(void)
{
FILE *f = fopen("testfile", "r");
if (fseek(f, 0, SEEK_END))
fprintf(stderr, "Errorn");
return 0;
}
$ gcc -O1 b.c -o b
您会注意到,对于这两种情况,我都使用了选项"-O1",它允许编译器引入小的优化,这主要是为了使机器代码更干净一些,因为如果没有优化,编译器会"字面上"转换为机器代码。
$ objdump -Mintel -D a |grep -i main -A20
0000000000001189 <main>:
1189: f3 0f 1e fa endbr64
118d: 48 83 ec 08 sub rsp,0x8
1191: 48 8d 35 6c 0e 00 00 lea rsi,[rip+0xe6c] # 2004 <_IO_stdin_used+0x4>
1198: 48 8d 3d 67 0e 00 00 lea rdi,[rip+0xe67] # 2006 <_IO_stdin_used+0x6>
119f: e8 dc fe ff ff call 1080 <fopen@plt>
# Interesting part
11a4: 48 89 c7 mov rdi,rax # Sets return of fopen as param 1
11a7: ba 02 00 00 00 mov edx,0x2 # Sets Ox2 (SEEK_END) as param 3
11ac: be 00 00 00 00 mov esi,0x0 # Sets 0 as param 2
11b1: e8 ba fe ff ff call 1070 <fseek@plt> # Call to FSEEK being made and stored in register
11b6: 85 c0 test eax,eax # Comparison being made
11b8: 75 0a jne 11c4 <main+0x3b> # Comparison jumping
# End of interesting part
11ba: b8 00 00 00 00 mov eax,0x0
11bf: 48 83 c4 08 add rsp,0x8
11c3: c3 ret
11c4: 48 8b 0d 55 2e 00 00 mov rcx,QWORD PTR [rip+0x2e55] # 4020 <stderr@@GLIBC_2.2.5>
11cb: ba 06 00 00 00 mov edx,0x6
11d0: be 01 00 00 00 mov esi,0x1
11d5: 48 8d 3d 33 0e 00 00 lea rdi,[rip+0xe33] # 200f <_IO_stdin_used+0xf>
11dc: e8 af fe ff ff call 1090 <fwrite@plt>
11e1: eb d7 jmp 11ba <main+0x31>
11e3: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0]
11ea: 00 00 00
11ed: 0f 1f 00 nop DWORD PTR [rax]
对二进制"b"进行 objdumping 会产生几乎相同的机器代码结果。总而言之,无论您在 if 语句中放入什么内容都会被计算,无论您是否先为其分配变量,都会产生一个 beind-the-scene 等效结果。
编辑:
作为参考,这是$ objdump -Mintel -D b |grep -i main -A20
的输出:
0000000000001189 <main>:
1189: f3 0f 1e fa endbr64
118d: 48 83 ec 08 sub rsp,0x8
1191: 48 8d 35 6c 0e 00 00 lea rsi,[rip+0xe6c] # 2004 <_IO_stdin_used+0x4>
1198: 48 8d 3d 67 0e 00 00 lea rdi,[rip+0xe67] # 2006 <_IO_stdin_used+0x6>
119f: e8 dc fe ff ff call 1080 <fopen@plt>
# Interesting Part
11a4: 48 89 c7 mov rdi,rax
11a7: ba 02 00 00 00 mov edx,0x2
11ac: be 00 00 00 00 mov esi,0x0
11b1: e8 ba fe ff ff call 1070 <fseek@plt>
11b6: 85 c0 test eax,eax
11b8: 75 0a jne 11c4 <main+0x3b>
# End of interesting part
11ba: b8 00 00 00 00 mov eax,0x0
11bf: 48 83 c4 08 add rsp,0x8
11c3: c3 ret
11c4: 48 8b 0d 55 2e 00 00 mov rcx,QWORD PTR [rip+0x2e55] # 4020 <stderr@@GLIBC_2.2.5>
11cb: ba 06 00 00 00 mov edx,0x6
11d0: be 01 00 00 00 mov esi,0x1
11d5: 48 8d 3d 33 0e 00 00 lea rdi,[rip+0xe33] # 200f <_IO_stdin_used+0xf>
11dc: e8 af fe ff ff call 1090 <fwrite@plt>
11e1: eb d7 jmp 11ba <main+0x31>
11e3: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0]
11ea: 00 00 00
11ed: 0f 1f 00 nop DWORD PTR [rax]
简短的回答是肯定的(就像在你的琐碎例子中一样(,长答案是也许。
当逻辑表达式(any(更复杂时,C语言会计算它,直到完全确定整个表达式的结果。不评估其余操作。
例子:
int x = 0;
if(x && foo()) {}
不会调用 FOO,因为x
是假的 - 然后整个操作是假的。
int x = 1;
if(x && foo()) {}
将调用 foo,因为x
为 true,并且需要表达式的第二部分才能获得结果。
它被称为短路评估,C 中的所有逻辑表达式都以这种方式计算。