4

我有以下 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。那里会发生什么?是否有某种临时的、更大的类型来包含结果?

4

4 回答 4

7

的操作数+被提升为更大的类型,我们可以通过草稿 C99 标准部分6.5.6 Additive operator看到这一点,其中说:

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

如果我们去6.3.1.8 通常的算术转换它说:

否则,对两个操作数都执行整数提升。

然后我们转到6.3.1.1 布尔值、字符和整数,上面写着(强调我的):

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

所以+在这种情况下,两个操作数都会被提升为int类型进行操作,所以不会溢出。

注意,为什么在 C 和 C++ 中的算术运算之前必须将 short 转换为 int?解释促销的理由。

于 2014-10-04T18:35:50.303 回答
3

第一个例子很明显,uint16_t 类型足够大,可以包含结果。

事实上,x赋值的目标左值与x = expr;是否存在溢出无关expr。如果有,那么无论有多宽,结果都是如此x

在您的示例中,“整数提升”适用,并且计算是在int操作数之间完成的。这意味着没有溢出。整数提升在 C11 的第 6.3.1.1:2 节中描述。

如果您一直在添加两个uint32_t值,那么可能会出现回绕(当无符号操作产生超出无符号类型范围的结果时的指定行为),即使要分配给结果的左值的类型是uint64_t

于 2014-10-04T18:36:09.393 回答
2

是的,所有算术运算都以至少具有int. 因此,您的操作数首先转换为int,然后执行操作。与您的第一个示例一样,然后将结果转换回分配的目标类型。

通常,对窄类型进行算术运算根本不是一个好主意。尽可能避免,它只会使事情复杂化。最好是完全避免这些类型,除非你有一个真正的问题来存储大量的数字,例如

于 2014-10-04T18:36:36.720 回答
0

int在 C 中,如果输入类型是 long 或更大的数据类型 ,则中间结果至少会更宽。

于 2014-10-04T18:26:47.017 回答