Является ли опция GCC -O2 нарушением этой небольшой программы или у меня есть поведение undefined
Я нашел эту проблему в очень большом приложении, сделал из нее SSCCE. Я не знаю, нарушает ли код поведение undefined или -O2
.
При компиляции с gcc a.c -o a.exe -O2 -Wall -Wextra -Werror
он печатает 5.
Но при компиляции без -O2
(например, -O1
) он печатает 25) или раскомментирует одну из двух прокомментированных строк (предотвратите inlining).
#include <stdio.h>
#include <stdlib.h>
// __attribute__((noinline))
int f(int* todos, int input) {
int* cur = todos-1; // fixes the ++ at the beginning of the loop
int result = input;
while(1) {
cur++;
int ch = *cur;
// printf("(%i)\n", ch);
switch(ch) {
case 0:;
goto end;
case 1:;
result = result*result;
break;
}
}
end:
return result;
}
int main() {
int todos[] = { 1, 0}; // 1:square, 0:end
int input = 5;
int result = f(todos, input);
printf("=%i\n", result);
printf("end\n");
return 0;
}
Является ли опция GCC -O2
нарушением этой маленькой программы или у меня есть поведение undefined где-то?
Ответы
Ответ 1
int* cur = todos-1;
вызывает поведение undefined. todos - 1
- недопустимый адрес указателя.
Акцент мой:
(C99, 6.5.6p8) "Если оба операнда указателя и результат указывают на элементы одного и того же объекта массива или один за последним элементом объекта массива, оценка не должна приводить к переполнению; в противном случае поведение undefined."
Ответ 2
В дополнении к ответу @ouah это объясняет, что делает компилятор.
Сгенерированный ассемблер для ссылки:
400450: 48 83 ec 18 sub $0x18,%rsp
400454: be 05 00 00 00 mov $0x5,%esi
400459: 48 8d 44 24 fc lea -0x4(%rsp),%rax
40045e: c7 44 24 04 00 00 00 movl $0x0,0x4(%rsp)
400465: 00
400466: 48 83 c0 04 add $0x4,%rax
40046a: 8b 10 mov (%rax),%edx
Однако, если я добавлю printf
в main()
:
400450: 48 83 ec 18 sub $0x18,%rsp
400454: bf 84 06 40 00 mov $0x400684,%edi
400459: 31 c0 xor %eax,%eax
40045b: 48 89 e6 mov %rsp,%rsi
40045e: c7 04 24 01 00 00 00 movl $0x1,(%rsp)
400465: c7 44 24 04 00 00 00 movl $0x0,0x4(%rsp)
40046c: 00
40046d: e8 ae ff ff ff callq 400420 <[email protected]>
400472: 48 8d 44 24 fc lea -0x4(%rsp),%rax
400477: be 05 00 00 00 mov $0x5,%esi
40047c: 48 83 c0 04 add $0x4,%rax
400480: 8b 10 mov (%rax),%edx
В частности (в версии printf
) эти две инструкции заполняют массив todo
40045e: c7 04 24 01 00 00 00 movl $0x1,(%rsp)
400465: c7 44 24 04 00 00 00 movl $0x0,0x4(%rsp)
Это явно отсутствует в версии не printf
, которая по какой-то причине присваивает только второй элемент:
40045e: c7 44 24 04 00 00 00 movl $0x0,0x4(%rsp)