在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
类型足以包含结果。 当我在OS / X上使用clang
编译器尝试第二个示例时,它正确返回true。 那里发生了什么? 是否有某种临时的 ,更大的类型来包含结果?
+
的操作数被提升为更大的类型,我们可以通过草拟C99标准 6.5.6
节添加运算符来看到这一点:
如果两个操作数都具有算术类型,则对它们执行通常的算术转换。
如果我们转到6.3.1.8
通常的算术转换,它会说:
否则,将对两个操作数执行整数提升。
然后我们转到6.3.1.1
布尔,字符和整数说( 强调我的 ):
如果int可以表示原始类型的所有值,则该值将转换为int; 否则,它将转换为unsigned int。 这些被称为整数促销 .48)所有其他类型由整数促销不变。
因此,在这种情况下, +
两个操作数都将被提升为操作类型int ,因此没有溢出。
注意, 为什么在C和C ++中的算术运算之前必须将short转换为int? 解释了促销的理由。
第一个例子很明显,uint16_t类型足以包含结果。
实际上,赋值x = expr;
的目标左值x
x = expr;
与expr
是否存在溢出无关。 如果有,那么结果就是它是什么,无论x
是多宽。
在您的示例中,“整数提升”适用,计算在int
操作数之间完成。 这意味着没有溢出。 整数促销在第6.3.1.1节第2节中的C11中描述。
如果你一直在添加两个uint32_t
值,那么可能有环绕(当无符号运算产生超出无符号类型的结果时指定的行为),即使要分配给结果的左值的类型也是如此是uint64_t
。
是的, 所有算术都是在至少宽度为int
的类型中完成的。 因此,您的操作数首先转换为int
,然后执行操作。 与第一个示例中一样,然后将结果转换回赋值的目标类型。
通常,使用窄类型进行算术通常不是一个好主意。 尽可能避免,只会使事情复杂化。 最好是完全避免这些类型,除非你有一个真正的问题来存储大数字数组,例如
在C中,如果输入类型为long或某个较大的数据类型,则中间结果至少为int
,更宽。