В языке C арифметические операции с целыми типами могут привести к переполнению (overflow), когда результат выходит за пределы представимого диапазона типа, например, int или unsigned int. Специфика поведения при переполнении накладывается стандартами языка.
Переполнение со знаковым типом (signed overflow) приводит к undefined behavior, то есть компилятор имеет право выполнить любые действия: игнорировать ошибку, сгенерировать исключение или оставить непредсказуемый результат. Для беззнаковых типов (unsigned) согласно стандарту C поведение при переполнении определено: происходит сброс по модулю размера типа (wraparound).
Для unsigned чисел результат переполнения легко прогнозируется, например, UINT_MAX + 1 == 0. Для signed чисел — рекомендуется перед операциями проверять границы типа с помощью макросов из <limits.h> или использовать инструменты статического анализа. Современные компиляторы и инструменты могут выявлять потенциальные переполнения.
Пример кода:
#include <stdio.h> #include <limits.h> int add_with_check(int a, int b) { if (a > 0 && b > INT_MAX - a) { printf("Произойдёт переполнение! "); return -1; } return a + b; } int main() { int x = INT_MAX, y = 1; printf("Результат: %d ", add_with_check(x, y)); unsigned int ux = UINT_MAX; printf("Unsigned overflow: %u ", ux + 1); return 0; }
Ключевые особенности:
<limits.h> для получения размеров типовЯвляется ли переполнение unsigned типа ошибкой?
Нет, это поведение определено стандартом и эквивалентно сбросу по модулю. Например, (unsigned int)UINT_MAX + 1 == 0 всегда истинно.
Можно ли положиться на то, что при переполнении int результат просто "перевалит" через INT_MIN?
Нет, такое поведение не гарантируется и не стандартизовано, это undefined behavior. Может crash-нуться, дать некорректное (разное на платформах) значение или быть оптимизировано компилятором непредсказуемым образом.
Можно ли рассчитывать на поведение, что int всегда двухкомплементарный?
Хотя современное железо практически всегда использует «two's complement» для представления signed int, язык C стандартно не требует этого, поэтому код с переполнением переносимым не будет.
Сложение чисел int без проверки границ — переполнение на больших данных приводит к невалидным расчётам.
Плюсы:
Минусы:
Перед всеми арифметическими операциями делается check на переполнение при помощи макросов и функций. Использование unsigned там, где wraparound допустим.
Плюсы:
Минусы: