Поведение целочисленного переполнения C при назначении целым числам большей ширины

Если я выполню следующий код на C:

#include <stdint.h>

uint16_t a = 4000;
uint16_t b = 8000;

int32_t c = a - b;

printf("%d", c);

В результате он правильно печатает «-4000». Однако я немного смущен: не должно ли быть арифметического переполнения при вычитании большего целого числа без знака из другого? Какие правила кастинга здесь действуют? Этот вопрос кажется немного нубским, поэтому любые ссылки будут очень признательны.


person Davorian    schedule 08.04.2009    source источник


Ответы (4)


Вопрос на самом деле несколько сложный. Операнды арифметических выражений преобразуются с использованием определенных правил, которые вы можете увидеть в разделе 3.2.1.5 документа Стандартный (C89). В вашем случае ответ зависит от типа uint16_t. Если он меньше int, скажем, short int, то операнды преобразуются в int, и вы получаете -4000, но в 16-битной системе uint16_t может быть unsigned int, и преобразование в знаковый тип не произойдет автоматически.

person TrayMan    schedule 08.04.2009
comment
Я ссылаюсь, конечно, на старый стандарт. См. ответ tgamblin для C99. Хотя говорят одно и то же. - person TrayMan; 08.04.2009
comment
3.2.1.5 предназначен для преобразований типа double/float/int/etc. Он ищет интегральные (long/int/char/short/etc) рекламные акции, которые есть в 3.2.1.1 этой версии стандарта. Это в 6.3.1.1 в более новом стандарте, на который я ссылался выше. - person Todd Gamblin; 08.04.2009
comment
Он также может захотеть 3.2.1.2 (целые со знаком/без знака) - person Todd Gamblin; 08.04.2009
comment
Раздел 3.2.1.5 также охватывает целочисленные преобразования. - person TrayMan; 08.04.2009
comment
о хм так и есть. Я думаю, у него есть правила для длинных чисел и тому подобного. Есть элемент указателя, который указывает вам на 3.2.1.1 для интегральных рекламных акций. - person Todd Gamblin; 08.04.2009
comment
Хорошо, а что, если мы расширим пример до двух uint32_t и одного int64_t, предполагая 32-битную ширину int? - person Davorian; 09.04.2009
comment
В этом случае вычитание беззнаковое и переполняется. - person TrayMan; 09.04.2009

Короткий ответ: все они повышаются до int во время вычитания. Подробный ответ см. в разделе 6.3.1.1 Стандарт C, где говорится о целочисленных преобразованиях в арифметических выражениях. Соответствующий язык из стандарта:

Если int может представлять все значения исходного типа, значение преобразуется в int; в противном случае он преобразуется в unsigned int. Они называются целочисленными акциями. Все остальные типы не изменяются целочисленными акциями.

Подробности там тоже есть, но они становятся довольно неприятными.

person Todd Gamblin    schedule 08.04.2009

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

person dirkgently    schedule 08.04.2009

На самом деле переполнение есть, но C вам об этом не сообщает.

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

Попробуйте интерпретировать результат как беззнаковый, и вы заметите, что (u1-u2) оценивается каким-то, казалось бы, несвязанным числом, когда u1 ‹ u2.

person Ingo    schedule 08.04.2009