假设我们有以下字节[4]:

44 a4 8a c6

因此,以下代码出了什么问题:

public static int asIntBigEndian(byte[] raw, int offset){
int result = 0;
for(int i=offset; i<offset+4; ++i){
    result = (result << 4) | raw[i];
}
return result;
}


调用asIntBigEndian(raw,0)的结果是:

ff ff ff e6

我注意到的是,如果我要读取第一个字节并将其打印出来,则会得到:

44

如果这样做,我将得到相同的结果:

System.out.println(Integer.toHexString(raw[0] << 24));


0x44000000

所以,如果我继续逻辑……

System.out.println(Integer.toHexString( (raw[0] << 24)|(raw[1] << 16) );


0xffa40000

基本上,第一个字节变为0xff,而第二个字节0xa4已被“异或”到正确的位置。为什么会这样呢?

最佳答案

Java中的byte的范围是-128(-0x80)到127(0x7F)。 164(0xA4)不是有效值,但是“ -A4”是通过打印-92(-0x5C)所获得的,就好像它是未签名的一样。

将-0x5C转换为int也会得到-0x0000005C。打印为无符号的-0x0000005C为0xFFFFFFA4。

考虑它的另一种可能更简单的方法是将所有值都视为无符号,但将转换视为符号扩展-将最高位复制到所有新位中。如果以这种方式考虑,则0xA4是有效字节,(int)0xA4是0xFFFFFFA4。结果相同,思考过程更简单,但是用Java思考数字的方式不太正确(不是那么重要)。

0xFFFFFFA4 << 16给出0xFFA40000,而0x44000000 | 0xFFA40000给出0xFFA40000-这就是得到结果的方式。

修复很简单-代替raw[i],使用((int)raw[i] & 0xFF)或只是(raw[i] & 0xFF),因为对int的转换是隐式的。

另外,与该问题无关,(result << 4)应该为(result << 8)。否则,您将计算0x44000 | 0xA400 | 0x8A0 | 0xC6而不是0x44000000 | 0xA40000 | 0x8A00 | 0xC6

10-04 10:18