Я получаю:
предупреждение: предполагается, что переполнение со знаком не происходит, если предположить, что (X + c) < X всегда ложно [-Wstrict-overflow]
на этой линии:
if ( this->m_PositionIndex[in] < this->m_EndIndex[in] )
m_PositionIndex
а также m_EndIndex
типа itk::Index
(http://www.itk.org/Doxygen/html/classitk_1_1Index.html), и их operator[]
возвращает signed long
,
(здесь строка 37: https://github.com/Kitware/ITK/blob/master/Modules/Core/Common/include/itkImageRegionConstIteratorWithIndex.hxx для контекста)
Кто-нибудь может объяснить, что вызвало бы это предупреждение? Я не вижу картины (x+c) < x
где угодно — так как это просто signed long
сравнение.
Я попытался воспроизвести это в отдельном примере:
#include <iostream>
namespace itk
{
struct Index
{
signed long data[2];
Index()
{
data[0] = 0;
data[1] = 0;
}
signed long& operator[](unsigned int i)
{
return data[i];
}
};
}
int main (int argc, char *argv[])
{
itk::Index positionIndex;
itk::Index endIndex;
for(unsigned int i = 0; i < 2; i++)
{
positionIndex[i]++;
if ( positionIndex[i] < endIndex[i] )
{
std::cout << "something" << std::endl;
}
}
return 0;
}
но я не получаю предупреждение там. Любые мысли о том, что отличается между моей демонстрацией и реальным кодом, или что может быть причиной предупреждения в реальном коде? Я получаю предупреждение как с gcc 4.7.0, так и с 4.7.2 с флагом -Wall.
Чтобы просто отключить это предупреждение, используйте -Wno-strict-overflow
, Чтобы вместо этого отключить определенную оптимизацию, которая вызывает это предупреждение, используйте -fno-strict-overflow
или же -fwrapv
,
gcc manpage описывает, что это предупреждение можно контролировать с помощью уровней: -Wstrict-overflow=n
,
Если это останавливает вашу сборку из-за -Werror
, вы можете обойти, не скрывая предупреждения с помощью -Wno-error=strict-overflow
(или просто -Wno-error
переопределить -Werror
).
Анализ и комментарии …
Я получил то же самое предупреждение и потратил пару часов, пытаясь воспроизвести его на небольшом примере, но так и не получилось. Мой реальный код включал вызов встроенной функции в шаблонном классе, но алгоритм упрощается до следующего …
int X = some_unpredictable_value_well_within_the_range_of_int();
for ( int c=0; c<4; c++ ) assert( X+c >= X ); ## true unless (X+c) overflows
В моем случае предупреждение было как-то связано с тем, что оптимизатор развернул for
цикл, так что я смог обойти, объявив volatile int c=0
, Еще одна вещь, которая исправила это было объявить unsigned int c=0
, но я не совсем уверен, почему это имеет значение. Еще одна вещь, которая исправила это, заключалась в том, что счетчик циклов был достаточно большим, чтобы цикл не был развернут, но это бесполезное решение.
Так что же на самом деле говорит это предупреждение? Либо он говорит, что оптимизатор изменил семантику вашего алгоритма (не допуская переполнения), либо он просто сообщает вам, что оптимизатор предполагает, что ваш код не имеет неопределенное поведение переполнения целого числа со знаком. Если переполнение целых чисел со знаком не является частью предполагаемого поведения вашей программы, это сообщение, вероятно, не указывает на проблему в вашем коде — так что вы, вероятно, захотите отключить его для обычных сборок. Если вы получили это предупреждение, но не уверены в рассматриваемом коде, возможно, безопаснее всего просто отключить оптимизацию с помощью -fwrapv
,
Кстати, я столкнулся с этой проблемой в GCC 4.7, но тот же код был скомпилирован без предупреждения с использованием 4.8 — возможно, это указывает на то, что разработчики GCC признали необходимость быть немного менее «строгим» в обычном случае (или, может быть, так и было просто из-за различий в оптимизаторе).
Философия …
В [C ++ 11:N3242$ 5.0.4], говорится …
Если во время вычисления выражения результат не
математически определены или нет в диапазоне представимых значений для
его тип, поведение не определено.
Это означает, что соответствующий компилятор может просто предположить, что переполнение никогда не происходит (даже для неподписанных типов).
В C ++ благодаря таким функциям, как шаблоны и конструкторы копирования, элизия бессмысленных операций является важной возможностью оптимизатора. Однако, иногда, если вы используете C ++ в качестве низкоуровневого «системного языка», вы, вероятно, просто хотите, чтобы компилятор делал то, что вам говорят, и полагался на поведение базового оборудования. Учитывая язык стандарта, я не уверен, как достичь этого независимо от компилятора.
Компилятор говорит вам, что у него достаточно статических знаний об этом фрагменте, чтобы знать, что тест всегда будет успешным, если он может оптимизировать тест, предполагая, что ни одна подписанная операция не будет переполнена.
Другими словами, единственный способ x + 1 < x
когда-либо вернет истину, когда x
подписано, если x
это уже максимальное значение со знаком. [-Wstrict-overflow]
давайте предупредим компилятор, когда он может предположить, что никакое подписанное дополнение не будет переполнено; в основном он говорит вам, что собирается оптимизировать тест, потому что переполнение со знаком — неопределенное поведение.
Если вы хотите подавить предупреждение, избавьтесь от теста.
Я думаю, что компилятор изменился
positionIndex[i]++;
if ( positionIndex[i] < endIndex[i] )
в нечто более оптимизированное, как
if ( positionIndex[i]++ < endIndex[i] )
так
if ( positionIndex[i] + 1 < endIndex[i] )
это вызывает неопределенное поведение в случае переполнения (спасибо Сету).
В моем случае это было хорошее предупреждение от компилятора. У меня была глупая ошибка копирования / вставки, когда у меня был цикл внутри цикла, и у каждого был один и тот же набор условий. Что-то вроде этого:
for (index = 0; index < someLoopLimit; index++)
{
// blah, blah, blah.....
for (index = 0; index < someLoopLimit; index++)
{
// More blah, blah, blah....
}
}
Это предупреждение сэкономило мне время на отладку. Спасибо, gcc !!!
Это почти наверняка ошибка, хотя я не могу сказать, какая из трех возможных ошибок.
Я полагаю, что gcc каким-то образом выясняет, из чего вычисляются значения в this-> m_PositionIndex [in] и т. Д., А именно, что оба значения получены из одного и того же значения, одно со смещением, которое также может оказаться положительным , Следовательно, он показывает, что одна ветвь конструкции if / else является недостижимым кодом.
Это плохо. Определенно. Зачем? Ну, есть ровно три возможности:
gcc прав в том, что код недоступен, и вы упустили из виду, что это так. В этом случае у вас просто раздутый код. Лучшая из трех возможностей, но все же плохая с точки зрения качества кода.
gcc прав в том, что код недоступен, но вы хотели, чтобы он был доступен. В этом случае вам нужно еще раз взглянуть на то, почему это не достижимо, и исправить ошибку в вашей логике.
GCC не так, вы наткнулись на ошибку в нем. Совершенно маловероятно, но возможно, и, к сожалению, очень трудно доказать.
Очень плохо то, что вам абсолютно необходимо либо выяснить, почему именно код недоступен, либо опровергнуть правильность gcc (что легче сказать, чем сделать). В соответствии с законом Мерфи, если предположить случай 1. или 3., не доказав, что он будет подтвержден, это был случай 2. и что вам придется охотиться за ошибкой во второй раз …