Постинкремент, вызовы функций, концепция точки следования в GCC

Есть фрагмент кода, в котором GCC выдает результат, которого я не ожидал:

(Я использую gcc версии 4.6.1 Ubuntu/Linaro 4.6.1-9ubuntu3 для цели i686-linux-gnu)

[тест.с]

#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??


person Chung-Ju Wu    schedule 15.08.2012    source источник
comment
Добро пожаловать в StackOverflow! Пожалуйста, не нумеруйте строки программ, которые может понадобиться скопировать-вставить для изучения.   -  person Pascal Cuoq    schedule 21.08.2012
comment
О~ Хорошо! Сейчас убрал количество строк программ. Спасибо за напоминание! :)   -  person Chung-Ju Wu    schedule 24.08.2012


Ответы (2)


Я сообщил об этой ошибке GCC некоторое время назад, и она была исправлена ​​ранее в этом году. См. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48814

person Johannes Schaub - litb    schedule 21.08.2012
comment
Спасибо! Кажется, это определенно ошибка в GCC 4.6.1. Я проверю последнюю версию GCC, соберу ее и снова попробую этот пример. Большое спасибо! :) - person Chung-Ju Wu; 24.08.2012

В дополнение к Clang есть два других инструмента, которые вы можете использовать в качестве справочных: анализ ценности Frama-C и KCC. Я не буду вдаваться в подробности того, как их установить или использовать для этой цели, но их можно использовать для проверки определенности программы на C — в отличие от компилятора, они предназначены для того, чтобы сообщать вам, проявляет ли целевая программа undefined. поведение.

У них есть свои шероховатости, но они оба считают, что b определенно должно быть 5 без неопределенного поведения в конце вашей программы:

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

Это еще более сильный аргумент, чем так думает Clang (поскольку, если бы это было неопределенное поведение, Clang все равно мог бы сгенерировать программу, которая печатает b = 5).

Короче говоря, похоже, вы нашли ошибку в этой версии GCC. Следующий шаг — проверить SVN, чтобы увидеть, присутствует ли он там.

person Pascal Cuoq    schedule 21.08.2012
comment
Спасибо! Это очень полезные инструменты для проверки программ! Кстати, Йоханнес Шауб сказал, что сообщил об этой ошибке, и она была исправлена ​​ранее в этом году. Я проверю последнюю версию GCC и попробую еще раз. :) - person Chung-Ju Wu; 24.08.2012