【问题标题】:'if' test condition in c - does it evaluate?c中的'if'测试条件 - 它是否评估?
【发布时间】:2020-09-30 07:30:38
【问题描述】:

当在 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

【问题讨论】:

  • 答案是肯定的。
  • does it evaluate exactly as if you had called it normally 虽然答案是(很可能)是的,但我不确定我是否完全遵循这个问题,即“完全符合”,而不是什么/如何?
  • @dxiv 问题解释说:“如中所示,除了返回值之外的所有影响都会评估并持续存在”。
  • fseek 返回 int,而不是 long int。您所拥有的将起作用,但将int 返回值转换为long int 并没有什么意义。使用不同的类型也会产生误导。
  • @user250102 完全不相关。 fseek 返回一个 int,它是 0-1。所以你应该将i 声明为int。将long 用于ftell。将int 用于fseek

标签: c function if-statement fseek


【解决方案1】:

是的,这完全一样。唯一的区别是您将无法再次使用 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, "Error\n");
    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, "Error\n");
    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 语句中添加什么,都会对其进行评估,并且无论您是否先为其分配变量,都会产生一个幕后等效结果。

编辑:

供参考,这是$ 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]

【讨论】:

    【解决方案2】:

    简短的答案是肯定的(就像你的小例子一样),长的答案是可能的。

    当逻辑表达式 (any) 更复杂时,C 语言会对其求值,直到整个表达式的结果完全确定。不评估剩余的操作。

    例子:

    int x = 0;
    
    if(x && foo()) {} 
    

    foo 不会被调用,因为x 为假 - 然后整个操作为假。

    int x = 1;
    
    if(x && foo()) {} 
    

    将调用 foo,因为 x 为真,并且需要表达式的第二部分来获得结果。

    它被称为Short circuit evaluation,C 中的所有逻辑表达式都以这种方式计算。

    【讨论】:

    • 这一切都是正确的,但只要在 any 上下文中评估这样的表达式,这同样适用。作为if 测试条件的一部分,它没有什么特别或不同之处。
    • @dxiv When the logical expression is more complex the C language evaluates it until the result of the whole expression is fully determined 我只写了ifs 吗?多么糟糕的挑剔。人们不阅读而是评论
    • @P__J__ 你说“长答案是可能的。”考虑到在“它是否像您正常调用它一样评估它”的问题的上下文中,人们可以轻松且正确地将其解释为“也许该条件的评估与您正常调用它时完全一样”。它认为这是 dxiv 的一个非常精明的观点。您的答案应该是 if 语句中的条件像任何其他表达式一样被评估,句号。没有关于它的也许。您对短路评估的解释很有帮助,但这并不是条件式的特殊警告。
    • @P__J__ 这个问题是关于“if 测试条件”的。此外,您似乎认为反对票是我的。不是。
    • @P__J__ 没错。实际上,您根本没有参考if 条件。我的意思是,您的答案是正确的,但与 OP 提出的问题不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    相关资源
    最近更新 更多