GCC中的后增量,函数调用,序列点概念

时间:2012-08-15 09:27:16

标签: c c99 gcc4 sequence-points

有一个代码片段,GCC会产生我没想到的结果:

(我正在使用gcc版本4.6.1 Ubuntu / Linaro 4.6.1-9ubuntu3用于目标i686-linux-gnu)

[test.c的]

#include <stdio.h>

int *ptr;

int f(void)
{
    (*ptr)++;

    return 1;
}

int main()
{
    int a = 1, b = 2;

    ptr = &b;

    a = b++ + f() + f() ? b : a;

    printf ("b = %d\n", b);

    return a;
}

根据我的理解,函数调用有一个序列点。 后增量应在f()之前进行。

见C99 5.1.2.3: “...称为序列点,以前评估的所有副作用  应完整,后续评估不得产生任何副作用  已经发生了。“

对于此测试用例,可能未指定评估顺序, 但最终的结果应该是一样的。所以我希望b的最终结果是5。 但是,在使用'gcc test.c -std = c99'编译此案例后,输出显示b = 3.

然后我用“gcc test.c -std = c99 -S”来看看发生了什么:

        movl    $1, 28(%esp)
        movl    $2, 24(%esp)
        leal    24(%esp), %eax
        movl    %eax, ptr
        movl    24(%esp), %ebx
        call    f
        leal    (%ebx,%eax), %esi
        call    f
        addl    %esi, %eax
        testl   %eax, %eax
        setne   %al
        leal    1(%ebx), %edx
        movl    %edx, 24(%esp)
        testb   %al, %al
        je      .L3
        movl    24(%esp), %eax
        jmp     .L4
.L3:
        movl    28(%esp), %eax
.L4:
        movl    %eax, 28(%esp)

似乎GCC在f()之前使用了评估值并执行'++' 两次f()调用后的操作。

我也使用llvm-clang来编译这个案例, 结果显示b = 5,这就是我的期望。

我的理解是否对后增量和序列点行为不正确? 或者这是GCC461的已知问题?

2 个答案:

答案 0 :(得分:4)

除了Clang之外,您还可以使用其他两个工具作为参考:Frama-C's value analysisKCC。我不会详细介绍如何安装它们或将它们用于此目的,但它们可用于检查C程序的定义 - 与编译器不同,它们旨在告诉您目标程序是否展示未定义行为。

他们有粗糙的边缘,但他们都认为b肯定是5,在程序结束时没有未定义的行为:

Mini:~/c-semantics $ dist/kcc ~/t.c
Mini:~/c-semantics $ ./a.out 
b = 5

这是一个比Clang更强烈的论点(因为如果它是未定义的行为,Clang仍然可以生成一个打印b = 5)的程序。

长话短说,看起来你在那个版本的GCC中发现了一个错误。下一步是检查SVN,看它是否仍然存在。

答案 1 :(得分:3)

我前段时间报告了这个海湾合作委员会的错误,并在今年早些时候修复了。见http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48814