Как получить предупреждение в GCC о переполнении целого числа без знака вместо переноса? - PullRequest
0 голосов
/ 23 февраля 2019

Тест ENV

  • Linux
  • Intel x86-64 GCC 8.2.1
  • Флаги включены: -Wextra -Wall -Wfloat-equal -Wundef -Wshadow -Winit-self -Wpointer-arith -Wcast-align -Wstrict-prototypes -Wstrict-overflow=5 -Wwrite-strings -Waggregate-return -Wcast-qual -Wswitch-default -Wswitch-enum -Wconversion -Wunreachable-code -Wformat=2 -pedantic -pedantic-errors -Werror-implicit-function-declaration -Wformat-security -fstrict-overflow
  • sizeof(long)равно 8.
  • sizeof(int) равно 4.

Пример 1, получено предупреждение, хорошо:

long x = 2147483647 * 3;

Пример 2, без предупреждения, не хорошо:

long x = 2147483647U * 3U; // Suffix U

или

unsigned int a = 2147483647;
unsigned int b = 3;
long x = a*b;

Пример 3, без предупреждения, но работает, как ожидалось:

long x = 2147483647L * 3L; // Suffix L

В примере 2 я знаючто это обход, а не целочисленное переполнение, но это те случаи, о которых компилятор не может предупредить?

Из стандарта:

(6.3.1.8)

В противном случае целочисленные преобразования выполняются для обоих операндов.Затем к продвигаемым операндам применяются следующие правила:

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

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

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

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

(6.5):

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


Начато использование Clang с флагом -fsanitize=unsigned-integer-overflow, который очень помогает с нежелательными значениями при переносе. Это не целочисленное переполнение, но не предполагаемое значение. Поскольку GCC до сих пор не поддерживаетпредупреждение как это, переходя к Clang.

1 Ответ

0 голосов
/ 23 февраля 2019

Переполнение целых чисел со знаком вызывает неопределенное поведение , в то время как переполнение целых чисел без знака хорошо определено.

Для целых чисел без знака переполнение происходит так, как если бы значения были вычислены по модулю на единицу больше максимального значенияданного типа.Другими словами, если тип имеет ширину n бит, то сохраняются только младшие биты n результата.Это на самом деле не переполнение, но называется wraparound .

Это прописано в разделе 6.5p9 :

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

Поскольку это поведение хорошо определено, компилятору не имеет смысла выдавать предупреждение.

В случае вашего второго примера:

long x = 2147483647U * 3U; 

Умножение выполняется для unsigned типов, поэтому математический результат 6442450941 оборачивается в 2147483645, что находится в диапазоне long.Здесь нет переполнения (только с циклом) и преобразования вне диапазона, поэтому предупреждения нет.

...