C语言类型转换和溢出常见错误
类型转化
C语言进行类型转换时,采用符号扩展还是零扩展,只取决与被扩展数的类型,与目的类型无关。
unsigned int a;
long long b;
long long c;
b = (long long) a;
c = (long long)(int) a;
b和c有本质的区别:
- b是由a零扩展得到,高32位为0
- c是由a符号扩展得到,高32位与a的最高位相同:a首先被解释为带符号数,再进行扩展
常见的误解:认为
c = (long long)(int) a中(int) a可能发生无符号数到有符号数的溢出,从而导致结果错误。实则不然,int与unsigned int只是解释不同,在机器层面上本质一样!
溢出
int a;
long long b;
long long c;
b = a * a;
c = (long long)a * (long long)a;
常见误解:b可以得到a*a的64位完整结果。但事实上,两个 int 类型相乘,结果仍然是 int 类型,只是把 int 结果赋值给 long long 类型的 b,当a*a溢出时,b得到错误的值;c得到a*a的64位正确结果
实际的应用
NEMU项目用C语言实现RSIC-V32位虚拟机,其中涉及到以下指令:
mulh有符号32位 × 有符号32位,结果为高 32 位mulhu无符号32位 × 无符号32位,结果为高 32 位mulhsu有符号32位 × 无符号32位,结果为高 32 位
正确的逻辑如下
//src1:源操作数1,src2:源操作数2,R(rd):目的操作数
//mulh
long long tem = (long long)(int)src1 * (long long)(int)src2;
R(rd) = (int)(tem >> 32);
//mulhu
long long tem = (unsigned long long)src1 * (unsigned long long)src2;
R(rd) = (int)(tem >> 32);
//mulhsu
long long tem = (long long)(int)src1 * (unsigned long long)src2;
R(rd) = (int)(tem >> 32);
