Случай из реального проекта, код ~10-летней выдержки. Когда-то давно забыл добавить return
в функцию, где имеется хитрая условная компиляция. Долгое время всё нормально работало, а сегодня вот пересобрал проект и поимел весёлых проблем. При проигрывании звука сегфолтилось, хотя вроде как в коде всё было нормально. Старые версии (собранные старым GCC) не сегфолтились. При отключенной оптимизации сегфолта тоже не было. Что тут можно ещё сказать? Спасибо Сталлману за gdb
, сильно удивился когда увидел вызов якобы вырезанной функции в нём. Ну и главное: читайте и анализируйте Warning’и, господа! Минимальный пример:
$ cat dead_code.cpp
// dead_code.cpp
#include <cstdio>
int stub_0();
int pxt_PlayWithCallback(int chan, int slot, char loop, void (*FinishedCB)(int, int));
int pxt_Play(int chan, int slot, char loop) {
#ifdef _PLS_NO_DEAD_CODE
if (stub_0()) {
fprintf(stderr, "!!!!! GOOD CODE !!!!!\n");
}
#else
return pxt_PlayWithCallback(chan, slot, loop, NULL);
#endif
}
int pxt_PlayWithCallback(int chan, int slot, char loop, void (*FinishedCB)(int, int)) {
fprintf(stderr, "????? DEAD CODE ?????\n");
return stub_0();
}
int stub_0() { return 42; }
int main(int argc, char *argv[]) {
return pxt_Play(-1, 20, 0);
}
// OK:
$ g++ dead_code.cpp
$ ./a.out
????? DEAD CODE ?????
// OK:
$ g++ -D_PLS_NO_DEAD_CODE dead_code.cpp
$ ./a.out
!!!!! GOOD CODE !!!!!
// WTF?:
$ g++ -O2 -D_PLS_NO_DEAD_CODE dead_code.cpp
$ ./a.out
!!!!! GOOD CODE !!!!!
????? DEAD CODE ?????
Segmentation fault (core dumped)
// WTF???:
$ g++ -O3 -D_PLS_NO_DEAD_CODE dead_code.cpp
$ ./a.out
!!!!! GOOD CODE !!!!!
!!!!! GOOD CODE !!!!!
...
!!!!! GOOD CODE !!!!!
!!!!! GOOD CODE !!!!!
Segmentation fault (core dumped)
А ведь довольно интересный простор за этим может скрываться. Ну право ведь, забыли return проставить, компилятор же по-дефолту return 0
впихнёт, верно? А я в этом был уверен.
P.S.
// Имеется предупреждение по-дефолту, отсутствует ret в конце функции, провал и сегфолт.
$ gcc --version
gcc (GCC) 10.2.1 20200723 (Red Hat 10.2.1-1)
// Предупреждение только с -Wall, ret в конце функции имеется, нет провала и сегфолта.
$ gcc --version
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39)
P.P.S. поиграться с компиляторами:
C++: https://gcc.godbolt.org/z/7ne8PM
C: https://gcc.godbolt.org/z/b6vqbK
Может кто-нибудь из профи подробно объяснить механизм такого поведения? Спасибо.
См. комментарии и ссылки в теме.