我遇到的代码来自一个似乎认为在结果为负时从另一个相同类型的整数中减去无符号整数的问题 . 因此,即使它恰好适用于大多数体系结构,这样的代码也是不正确的 .
unsigned int To, Tf;
To = getcounter();
while (1) {
Tf = getcounter();
if ((Tf-To) >= TIME_LIMIT) {
break;
}
}
这是我能找到的C标准中唯一含糊不清的引用 .
涉及无符号操作数的计算永远不会溢出,因为无法通过结果无符号整数类型表示的结果将以比结果类型可以表示的最大值大1的数量为模 .
我想人们可以接受这个引用来表示当右操作数较大时,操作被调整为在模数截断数字的上下文中有意义 .
即
0x0000 - 0x0001 == 0x 1 0000 - 0x0001 == 0xFFFF
而不是使用依赖于实现的签名语义:
0x0000 - 0x0001 ==(无符号)(0 -1)==(0xFFFF但也是0xFFFE或0x8001)
哪种或哪种解释是对的?是否定义了?
4 回答
在无符号类型中生成负数的减法结果是明确定义的:
如您所见,
(unsigned)0 - (unsigned)1
等于-1模UINT_MAX 1,换句话说,UINT_MAX .请注意,虽然它确实说"A computation involving unsigned operands can never overflow",这可能会让您认为它仅适用于超过上限,但这是作为句子的实际绑定部分的动机:"a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type."此短语不限于上层溢出类型的边界,并且同样适用于太低而无法表示的值 .
使用无符号类型时,modular arithmetic(也称为"wrap around"行为)正在发生 . 要理解这种模块化算法,只需看看这些时钟:
9 + 4 = 1 (13 mod 12),所以到另一个方向是: 1 - 4 = 9 ( - 3 mod 12) . 使用无符号类型时应用相同的原则 . 如果 result type 是
unsigned
,则进行模运算 .现在看一下将结果存储为
unsigned int
的以下操作:如果要确保结果为
signed
,则将其存储到signed
变量中或将其转换为signed
. 当你想得到数字之间的差异并确保不会应用模运算时,你应该考虑使用_2583017中定义的abs()函数:要非常小心,特别是在写条件时,因为:
but
那么,第一种解释是正确的 . 但是,你在这种情况下对“签名语义”的推理是错误的 .
再次,你的第一个解释是正确的 . 无符号算术遵循模运算的规则,这意味着
0x0000 - 0x0001
对于32位无符号类型求值为0xFFFF
.但是,第二种解释(基于"signed semantics"的解释)也需要产生相同的结果 . 即即使您在有符号类型的域中评估
0 - 1
并获得-1
作为中间结果,此-1
仍然需要生成0xFFFF
,以后它将转换为无符号类型 . 即使某些平台对有符号整数使用奇异表示(1的补码,有符号幅度),在将有符号整数值转换为无符号整数值时,仍需要使用该平台来应用模运算规则 .例如,这个评估
仍然保证在
c
中产生UINT_MAX
,即使该平台使用外来表示的有符号整数 .对于无符号数字
unsigned int
或更大,在没有类型转换的情况下,a-b
被定义为产生无符号数,当添加到b
时,将产生a
. 将负数转换为无符号定义为产生数字,当加到符号反转的原始数字时,将产生零(因此将-5转换为无符号将产生一个值,当加到5时,将产生零) .请注意,小于
unsigned int
的无符号数字可能会得到在减法之前提升为int
类型,a-b
的行为将取决于int
的大小 .