C 中的"如果"测试条件 - 它是否评估?



当在 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 中的所有逻辑表达式都以这种方式计算。

最新更新