Что происходит, когда в выражении C происходит целочисленное переполнение?

У меня есть следующий код C:

uint8_t firstValue = 111;
uint8_t secondValue = 145;
uint16_t temp = firstValue + secondValue;
if (temp > 0xFF) {
    return true;
}
return false;

Это альтернативная реализация:

uint8_t firstValue = 111;
uint8_t secondValue = 145;
if (firstValue + secondValue > 0xFF) {
    return true;
}
return false;

Первый пример очевиден, uint16_t Тип достаточно большой, чтобы содержать результат. Когда я попробовал второй пример с clang компилятор на OS/X, он правильно вернул true. Что там происходит? Есть ли какой-то временный, больший тип, чтобы содержать результат?

4 ответа

Решение

Операнды + продвигаются к более крупным типам, мы можем увидеть это, перейдя к проекту стандартного раздела C99 6.5.6 Аддитивные операторы, которые говорят:

Если оба операнда имеют арифметический тип, над ними выполняются обычные арифметические преобразования.

и если мы пойдем в 6.3.1.8 Обычные арифметические преобразования это говорит:

В противном случае целочисленные продвижения выполняются для обоих операндов.

а потом мы идем к 6.3.1.1 Булево, символы и целые числа, которые говорят (выделение мое):

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

Так что оба операнда + в этом случае для операции будет введен тип int, поэтому переполнения нет.

Обратите внимание: почему короткое число должно быть преобразовано в целое число перед арифметическими операциями в C и C++? объясняет обоснование для продвижения по службе.

Первый пример очевиден, тип uint16_t достаточно большой, чтобы содержать результат.

На самом деле пункт назначения lvalue x для назначения x = expr; не имеет никакого отношения к тому, есть ли переполнение в expr, Если есть, то результатом является то, что он есть, независимо от того, насколько широко x является.

В вашем примере применяются "целочисленные рекламные акции", и вычисление выполняется между int операнды. Это означает, что переполнения нет. Целочисленные продвижения описаны в C11 в пункте 6.3.1.1:2.

Если бы вы добавили два uint32_t значений, то можно было бы выполнить обтекание (указанное поведение, когда беззнаковая операция выдает результат, выходящий за пределы для беззнакового типа), даже если тип lvalue, который нужно назначить для результата, был uint64_t,

Да, вся арифметика выполняется в типе с шириной не менее int, Таким образом, ваши операнды сначала преобразуются в int и затем операция выполняется. Как и в первом примере, результат затем преобразуется обратно в целевой тип назначения.

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

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

Другие вопросы по тегам