当C表达式发生整数溢出时会发生什么?

5

我有以下的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。那里发生了什么?是否有一种临时的更大的类型来容纳结果?


对于无符号类型,结果是“应该是什么”模接收变量的大小。我的意思是:只存储适合的位; 其余的(如果有)将被忽略。对于有符号类型,结果是未定义的。 - wildplasser
4个回答

8
+的操作数将提升为更大的类型,我们可以通过查看C99标准草案6.5.6加法运算符来了解这一点,其中写道:

如果两个操作数具有算术类型,则对它们执行通常的算术转换。

如果我们进入6.3.1.8通常的算术转换,则会发现:

否则,在两个操作数上执行整数提升。

然后我们进入6.3.1.1 布尔、字符和整数,其中写道(重点在此):

如果一个int可以表示原始类型的所有值,则该值转换为int; 否则,它将转换为unsigned int。 这些被称为整数提升。48) 所有其他类型都不受整数提升的影响。

因此,在此情况下,+的两个操作数将被提升为类型int进行操作,因此没有溢出。

请注意,为什么必须在C和C++中对short进行算术运算之前将其转换为int?解释了提升的原理。


3
第一个例子很明显,uint16_t类型足够大可以容纳结果。
事实上,赋值语句x = expr中的目标lvalue x与expr是否溢出无关。如果有溢出,则结果无论如何都是它自己,无论x有多宽。
在您的示例中,“整数提升”适用,并且计算是在int操作数之间完成的。这意味着没有溢出。整数提升在C11的clause 6.3.1.1:2中描述。
如果您正在添加两个uint32_t值,则即使要分配结果的lvalue的类型为uint64_t,也可能会发生环绕(当无符号操作产生超出无符号类型边界的结果时指定的行为)。

+1 你和 Jens 真的在我之后立刻回答了,我实际上等着看是否有人会跳出来给出更好的答案,我最初只打算留下评论。 - Shafik Yaghmour

2

是的,所有算术运算都是在至少具有int宽度的类型中完成的。因此,您的操作数首先被转换为int,然后执行操作。就像您的第一个示例一样,结果然后转换回分配的目标类型。

通常情况下,使用窄类型进行算术运算并不是一个好主意。除非您需要存储大量数字数组等问题,否则请避免使用这些类型,因为这只会使事情更加复杂。


0
在C语言中,中间结果至少以int的形式进行计算,如果输入类型为long或其他更大的数据类型,则会使用更宽的类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接