Код C ++ странным образом пропускается без каких-либо оптимизаций. Есть идеи почему? - PullRequest
1 голос
/ 22 января 2011

Я два дня искал ответ на этот вопрос, но безуспешно.Я никогда раньше не сталкивался с этой проблемой, поэтому постараюсь.Пожалуйста, потерпите меня.

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

file.h

...
short id;
...

file.cc

id = 0;
while (id < some_large_number)
{
   id = foo();  
   if (id == 2)
   {
      //do something
   }
   else if (id == 2900)
   {
      //do something
   }
   else if (id == 30000)
   {
      //do something
   }
   else if (id == 40000)
   {
      //do something
   }
   else if (id == 45000)
   {
      //do something
   }
   else
   {
      //do something else
   }
}

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

Поскольку я пытался пошагово пройти по коду с помощью GDB (без оптимизации), я заметил, что GDB будет переходить прямо к оператору else после достижения if (id == 30000) каждый раз.Поскольку числа были c макросами в шестнадцатеричной записи, я сначала не заметил, что 40000 выходит за пределы signed short.Это вводило в заблуждение и потратило много времени, пытаясь понять это: я перекомпилировал внешние библиотеки, переустановил g ++, среди прочего.

Очевидно, что id unsigned short устранило проблему.Другая проблема кажется проблемой компилятора.Но я до сих пор не понимаю, почему эти части кода были полностью пропущены во время выполнения и без оптимизации?Почему бы не пройти через все операторы if, чтобы я мог определить реальную проблему?Есть идеи?

Большое спасибо.Я надеюсь, что это нормально для первого вопроса.

Ответы [ 5 ]

3 голосов
/ 22 января 2011

Если вы включите все предупреждения от gcc, он сообщит вам во время компиляции, что это произойдет.

1 голос
/ 22 января 2011

short имеет длину 16 битов, а его диапазон составляет от -32768 до 32767. Таким образом, он никогда не может быть 40000 или 45000, и компилятор исключает мертвый код (так как он никогда не будет достигнут).

1 голос
/ 22 января 2011

Напомним, что во время компиляции компилятор C ++ просматривает код и определяет, какие части кода выполняются в каком порядке. Если компилятор определит, что часть кода никогда не будет запущена, он не будет ее оптимизировать.

Например:

int i = 0;
if( i == 1) {
    printf("This will never be printed\n");
}

Здесь нет причин оптимизировать оператор if, поскольку он никогда не будет выполнен.

Этот тип экземпляра будет выбран, если вы скомпилируете:

g++ -Wall mycode.c

, где -Wall означает показывать все предупреждения, а mycode.c - файл вашего проекта.

Что касается выполнения, шаг через GDB показывает текущий поток программы. Если ветвь (в операторе if) является ложной, зачем она будет проходить через этот раздел кода? Вы можете использовать только одну ветвь в операторе if-elseif-else.

Надеюсь, это поможет вам.

1 голос
/ 22 января 2011

GCC является отличным оптимизирующим компилятором, однако даже когда информация об ошибках и предупреждениях включена через -Werror, -Wall и т. Д. GCC по-прежнему не предоставляет тот же уровень информации, что и диагностическая компиляция. При разработке кода я бы рекомендовал использовать диагностический компилятор Clang, чтобы помочь в поиске ошибок и ошибок. Clang предназначен для совместимости с GCC, и, за исключением некоторых более эзотерических функций, у меня не возникло проблем при смене моего CC между этими двумя в моем Makefile.

Будучи оптимизирующим компилятором, я считаю, что GCC по умолчанию включает устранение мертвого кода. Это приведет к тому, что все ветви, которые компилятор обнаружил невозможными, например, те, которые находятся за пределами вашей переменной id, будут удалены. Возможно, вы сможете отключить этот тип устранения мертвого кода.

1 голос
/ 22 января 2011

Мой вывод такой же, как и у вас: кажется, что он был оптимизирован даже без включенной «оптимизации».Возможно, эти постоянные предикаты «всегда истинно» / «всегда ложно» используются для пропуска кода где-то непосредственно на этапе генерации кода, т. Е. Намного раньше, чем выполняются оптимизации -O переключателя.Просто предположение.

...