如何安全地将Java字节用作unsigned char?

时间:2015-07-04 05:21:15

标签: java c bit-manipulation unsigned

我正在移植一些在Java中使用大量位操作的C代码。 C代码在假设int为32位宽且char为8位宽的情况下运行。其中有断言检查这些假设是否有效。

我已经接受了以下事实:我必须使用long代替unsigned int。但我可以安全地使用byte代替unsigned char吗?

它们只代表字节,但我已经遇到了这个奇怪的事件:(data是C中的unsigned char *和Java中的byte[]

/* C */
uInt32 c = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];

/* Java */
long a = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]) & 0xffffffff;
long b = ((data[0] & 0xff) << 24) | ((data[1] & 0xff) << 16) |
          ((data[2] & 0xff) << 8) | (data[3] & 0xff) & 0xffffffff;

你会认为左移操作是安全的。但是,如果a中的某些字节为“否定”(b,则由于Java中奇怪的一元促销规则,datab不会相同正确的结果)。

我应该注意哪些其他“陷阱”?我真的不想在这里使用short

2 个答案:

答案 0 :(得分:3)

如果在计算中使用它之前确保将其值与255(或0xFF)进行按位和运算,则可以安全地使用byte来表示0到255之间的值。这会将其提升为int,并确保提升的值介于0到255之间。

否则,使用符号扩展名进行整数提升会导致-int值介于-128和127之间。 -127作为byte(十六进制0x81)将变为-127作为int(十六进制0xFFFFFF81)。

所以你可以这样做:

long a = (((data[0] & 255) << 24) | ((data[1] & 255) << 16) | ((data[2] & 255) << 8) | (data[3] & 255)) & 0xffffffff;

请注意,此处不需要第一个& 255,因为后面的步骤无论如何都会掩盖额外的位(& 0xffffffff)。但是,总是要包含它可能是最简单的。

答案 1 :(得分:-1)

  

...我可以安全地使用byte代替unsigned char吗?

正如你所发现的,不是真的......不。

根据Oracle Java documentationbyte是有符号整数类型,虽然它有256个不同的值(由于显式范围规范“它的最小值为-128,并且最大值127(含)“来自文档)有一些值可以存储来自C的unsigned char,而来自Java的byte不能(反之亦然)。

这解释了您遇到的问题。但是,问题的严重程度尚未在您的8位字节实现中得到充分证明。

  

我应该注意哪些“陷阱”?

虽然Java中的byte只需要支持(包括)-128和127之间的值,但C unsigned char具有最大值(UCHAR_MAX),这取决于用于表示它的位数(CHAR_BIT;至少为8)。因此,当CHAR_BIT大于8时,unsigned char可以存储超过255的额外值。

总之,在Java的世界中,byte实际上应该被称为octet(一组八位),其中 - 就像在C a byte ({ {1}},charsigned char)是一组至少(可能超过)8位

没有。他们并不等同。我不认为你会在Java中找到一个等价的类型;他们都是固定宽度。您可以安全地使用Java中的unsigned char作为C中byte的等效项(但除了int8_t之外不需要存在int8_t除外)。

至于陷阱,你的C代码中也有一些。假设CHAR_BIT == 8data[0]的任何系统上都是unsigned chardata[0] << 24 is undefined behaviour