x86_64平台下C语言uint16_t类型溢出运算的代码差异问询
x86_64平台下C语言uint16_t类型溢出运算的代码差异问询
嘿,咱们来好好掰扯一下这两段代码在x86_64平台上的区别哈!首先先明确前提:你的平台是x86_64,定义了三个uint16_t类型的变量:
uint16_t a, b, c;
先把两段代码摆出来:
代码片段(1)
uint16_t tmp = b - a; uint16_t result1 = c - tmp;
代码片段(2)
uint16_t result2 = c - (b - a);
接下来分析核心差异,关键在于整数提升规则和无符号整数溢出的处理时机:
对于代码(1):
- 计算
b - a时,uint16_t类型的a和b会被自动提升为int(x86_64的int是32位,完全能容纳uint16_t的所有取值范围),得到一个int类型的结果; - 把这个
int结果赋值给tmp(uint16_t)时,会自动执行模2^16运算,也就是把超出uint16_t范围的部分截断,相当于无符号整数的溢出处理; - 计算
c - tmp时,c和tmp同样先提升为int,计算后再赋值给result1,再次执行模2^16截断。
- 计算
对于代码(2):
- 同样,
b - a会提升为int计算,c也会提升为int; - 整个表达式
c - (b - a)是在int类型下完成计算的,全程没有中间截断; - 最后把
int类型的结果赋值给result2时,才执行一次模2^16的截断。
- 同样,
那结果会有不同吗?在x86_64平台上,因为int是32位,完全能容纳uint16_t运算的所有中间结果(比如b - a的范围是-65535到65535,c - (b - a)的范围是-131070到131070,都远在32位int的容纳范围内),所以最终result1和result2的结果其实是完全一致的!
不过中间过程的运算类型是不一样的:代码(1)有两次无符号截断,代码(2)只有一次。但因为x86_64的int足够大,中间不会出现有符号溢出,所以最终结果没区别。如果换成int是16位的老平台,那两段代码的结果可能就不一样了,但x86_64上不会。
举个实际例子验证下:假设a=1,b=0,c=0:
- 代码(1):
b - a是int类型的-1,赋值给tmp变成uint16_t的65535;然后0 - 65535是int的-65535,赋值给result1变成1(因为-65535 + 65536=1)。 - 代码(2):
0 - (0-1)是int的1,赋值给result2直接是1,结果一致。
再换个极端点的例子:a=65535,b=1,c=0:
- 代码(1):
b - a是int的-65534,赋值给tmp变成2;0-2是int的-2,赋值给result1变成65534。 - 代码(2):
0 - (1-65535)是int的65534,赋值给result2也是65534,结果还是一致。
所以总结一下:在x86_64平台上,这两段代码的最终运行结果没有差异,但运算的中间过程不同——代码(1)多了一次中间变量的无符号截断,代码(2)则是全程用32位int计算后再截断。
备注:内容来源于stack exchange,提问作者Nan Xiao
相关产品推荐
相关产品推荐

