Поведение целочисленного переполнения C при присвоении целым числам большей ширины
-
05-09-2019 - |
Вопрос
Если я выполню следующий код на C:
#include <stdint.h>
uint16_t a = 4000;
uint16_t b = 8000;
int32_t c = a - b;
printf("%d", c);
В результате он правильно печатает «-4000».Однако я немного в замешательстве:не должно ли быть арифметического переполнения при вычитании большего целого числа без знака из другого?Какие правила кастинга здесь действуют?Этот вопрос кажется немного нубским, поэтому любые ссылки будут очень признательны.
Решение
На самом деле вопрос несколько сложный.Операнды арифметических выражений преобразуются по особым правилам, которые вы можете увидеть в разделе 3.2.1.5 руководства. Стандартный (C89).В вашем случае ответ зависит от типа uint16_t
является.Если оно меньше int
, сказать short int
, то операнды преобразуются в int
и вы получите -4000, но на 16-битной системе, uint16_t
может быть unsigned int
и преобразование в подписанный тип не произойдет автоматически.
Другие советы
Короткий ответ: все они повышены до int
во время вычитания.Подробный ответ см. в разделе 6.3.1.1 руководства. Стандарт С, где говорится о целочисленные акции в арифметических выражениях.Соответствующий язык из стандарта:
Если
int
может представлять все значения исходного типа, значение преобразуется вint
;в противном случае он преобразуется вunsigned int
.Они называются целочисленные акции.Все остальные типы не изменяются в целочисленных акциях.
Детали тоже есть, но они довольно неприятные.
Оба операнда повышаются до int32_t
во время вычитания.Если бы результат был больше максимального значения для int32_t
вы бы увидели переполнение.
На самом деле переполнение есть, но C вам об этом не сообщает.
Переполнение оставляет значение, которое оказывается равным -4000, если интерпретировать его как целое число со знаком.Это работает так, как задумано на машинах с двумя комплементами.
Попробуйте интерпретировать результат как беззнаковый, и вы заметите, что (u1-u2) оценивается каким-то, казалось бы, несвязанным числом, когда u1 < u2.