假设我有以下C代码.
unsigned int u = 1234; int i = -5678; unsigned int result = u + i;
什么隐式转换在这里怎么回事,这是代码为安全的所有值u
和i
?(安全,从某种意义上说,即使此示例中的结果会溢出到某个巨大的正数,我也可以将其转换回int并获得真实的结果.)
简答
你i
将被转化通过添加为无符号整数UINT_MAX + 1
,则除了将与无符号值进行,产生大的result
(取决于的值u
和i
).
答案很长
根据C99标准:
6.3.1.8通常的算术转换
如果两个操作数具有相同的类型,则不需要进一步转换.
否则,如果两个操作数都具有有符号整数类型或两者都具有无符号整数类型,则具有较小整数转换等级类型的操作数将转换为具有更高等级的操作数的类型.
否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的秩,然后用带符号的整数类型的操作数被转换成无符号整数类型的操作数的类型.
否则,如果用符号整型操作数的类型,可以表示所有与无符号整数类型的操作数的类型的值的,然后用无符号整数类型的操作数被转换成符号整型操作数的类型.
否则,两个操作数都转换为无符号整数类型,对应于带有符号整数类型的操作数的类型.
在您的情况下,我们有一个unsigned int(u
)和signed int(i
).参考上面的(3),由于两个操作数具有相同的等级,i
因此需要将其转换为无符号整数.
6.3.1.3有符号和无符号整数
当具有整数类型的值转换为除_Bool之外的另一个整数类型时,如果该值可以由新类型表示,则它将保持不变.
否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内.
否则,新类型将被签名,并且值无法在其中表示; 结果是实现定义的,或者引发实现定义的信号.
现在我们需要参考上面的(2).您i
将通过添加转换为无符号值UINT_MAX + 1
.因此,结果将取决于UINT_MAX
您的实现的定义方式.它会很大,但不会溢出,因为:
6.2.5(9)
涉及无符号操作数的计算永远不会溢出,因为无法通过生成的无符号整数类型表示的结果将以比结果类型可以表示的最大值大1的数量为模.
额外奖励:算术转换半WTF
#includeint main(void) { unsigned int plus_one = 1; int minus_one = -1; if(plus_one < minus_one) printf("1 < -1"); else printf("boring"); return 0; }
您可以使用此链接在线试用:http://codepad.org/yPhYCMFO
额外奖励:算术转换副作用
算术转换规则可用于UINT_MAX
通过初始化无符号值来获取值-1
,即:
unsigned int umax = -1; // umax set to UINT_MAX
由于上述转换规则,无论系统的带符号数表示如何,这都保证是可移植的.有关更多信息,请参阅此SO问题:使用-1将所有位设置为true是否安全?
转化率从符号到无符号确实不是一定只复制或重新解释的符号值的表示.引用C标准(C99 6.3.1.3):
当具有整数类型的值转换为除_Bool之外的另一个整数类型时,如果该值可以由新类型表示,则它将保持不变.
否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内.
否则,新类型将被签名,并且值无法在其中表示; 结果是实现定义的,或者引发实现定义的信号.
对于现在几乎普遍的二进制补码表示,规则确实对应于重新解释位.但对于其他表示(符号和幅度或1'补码),C实现仍必须安排相同的结果,这意味着转换不能只复制位.例如,(无符号)-1 == UINT_MAX,无论表示如何.
通常,C中的转换被定义为对值进行操作,而不是对表示进行操作.
回答原来的问题:
unsigned int u = 1234; int i = -5678; unsigned int result = u + i;
i的值转换为unsigned int,yielding UINT_MAX + 1 - 5678
.然后将该值添加到无符号值1234,产生UINT_MAX + 1 - 4444
.
(与无符号溢出不同,有符号溢出会调用未定义的行为.环绕声是常见的,但C标准无法保证 - 并且编译器优化会对代码造成严重破坏,从而产生无根据的假设.)