компилятор g++: флаг оптимизации добавляет предупреждающее сообщение

Я заметил это интересное поведение компилятора g++, если я добавлю флаг-O3 в компилятор, я получу

otsu.cpp:220: warning: ‘x’ may be used uninitialized in this function

однако, когда я не использую оптимизацию и вместо этого использую флаг отладки-g, я не получил никаких предупреждений вообще. Теперь я больше доверяю компилятору, когда флаг-g включен; однако мне интересно, является ли это хорошо определенным поведением, которое следует ожидать?

для ясности, код, который вызывает это что-то вдоль этих строки:

int x; //uninitialized


getAValueForX( &x ); // function makes use of x,
                     // but x is unitialized

здесь

 void getAValueForX( int *x )
 {
     *x = 4;
 }

или что-то в этом роде, очевидно, более сложное.

7 ответов


Это ожидалось. Оптимизация приводит к запуску определенного анализа кода, и именно так gcc находит неинициализированные переменные. Это на странице руководства:

. . . эти предупреждения зависят от оптимизации

http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html


Это на самом деле очень распространено с gcc. И да, этого следовало ожидать.

насколько я понял, для оптимизации компилятор генерирует много метрик и преобразует код (или, точнее, представление кода) таким образом, что позволяет обнаруживать неинициализированные или неиспользуемые переменные для примеров (есть несколько других предупреждений, подобных этому, я не помню список).

выполнение того же самого без оптимизации потребует делаю, а потом отбрасываю весь этот анализ. Это значительно замедлит компиляцию без какой-либо хорошей цели (тем более, что в debug компилятор не должен переставлять код).


анализ потока кода, выполняемый оптимизатором, позволяет ему обнаруживать потенциальные проблемы, которые не может обнаружить обычная (и более быстрая) компиляция. Проблема была всегда, компилятор просто не проверял ее.

даже если это предупреждение, это на самом деле может и не быть проблемой из-за фактического использования функции на практике; компилятор будет считать, что все возможные значения типов аргументов (и все внешние переменные, используемые в функции) может встречаются во всех возможных комбинациях, приводя по крайней мере к одному пути, где переменная используется без присвоения значения. Фактическое использование будет иметь гораздо более ограниченный набор возможных состояний, поэтому путь может никогда не возникнуть на практике. Простое решение - просто инициализировать переменную, если только закрыть компилятор - это ничего не будет стоить.

Я всегда использую оптимизатор как форму статического анализа бедняка, даже когда я в конечном счете не намерен использовать его в рабочий код. Точно так же я часто использую более одного компилятора по той же причине. Некоторые компиляторы выполняют проверки, которые другие не выполняют, или они генерируют сообщения с разными словами для одних и тех же ошибок, что часто помогает в их интерпретации для некоторых из более тупых сообщений.

цитата:

Я больше доверяю компилятору, когда-g флаг включен

Хотя верно, что если компилятор имеет ошибку, она, вероятно, будет в оптимизаторе (это будучи самой сложной частью), для зрелого компилятора, такого как GCC, это было бы очень редкой находкой. И наоборот, люди часто обнаруживают, что их рабочий код терпит неудачу при оптимизации; чаще всего код всегда был ошибочным (возможно, он полагался на неопределенное или определенное компилятором поведение), и оптимизатор только что выявил этот недостаток. Поэтому я предлагаю, если вы обнаружите, что ваш код ломается при оптимизации, заподозрить код до применения компилятора - бритвы Оккама.


мои флаги компилятора:

CFLAGS=-W -Wall\
 -Wno-non-template-friend\
 -Wold-style-cast\
 -Wsign-promo\
 -Wstrict-null-sentinel\
 -Woverloaded-virtual
# -Weffc++

- Weffc++ может быть очень раздражающим, поэтому иногда я пытаюсь, но обычно я его выключаю. Попробуйте эти - и другие в руководстве - и давайте посмотрим, что мы видим.


Да, это хорошо определенное поведение. Когда оптимизатор GCC не включен, он не выполняет определенные типы проверки пути выполнения (чтобы избежать штрафа за производительность при выполнении этих видов проверок). Определенных ситуациях, таких как использование переменных uninitialzed, могут быть обнаружены только при этих дополнительных проверок. Следовательно, с -O0, GCC не может предупредить об этих условиях.


ну, тот факт, что компилятор может перемещать вещи для оптимизации, может вызвать проблемы и привести к неопределенному поведению (как указано в ручных состояниях ниже); я думаю, было бы полезно увидеть код, чтобы попытаться помочь иметь смысл.

ярлыки, принятые оптимизированным кодом может иногда производить удивительные результаты: некоторые объявленные вами переменные может вообще не существовать; поток управления может ненадолго переместиться туда, где вас не было ожидайте этого; некоторые утверждения могут не быть выполняется, потому что они вычисляют константу результаты или их значения уже были под рукой; некоторые операторы могут выполняться в разных местах, потому что они были вышел из петли.


У меня такая же проблема в моем компиляторе msvc 6. Инициализация рассматриваемой переменной устраняет возможность неправильного пути с точки зрения компилятора.