我正在移植一些在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中奇怪的一元促销规则,data
和b
不会相同正确的结果)。
我应该注意哪些其他“陷阱”?我真的不想在这里使用short
。
答案 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 documentation,byte
是有符号整数类型,虽然它有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}},char
,signed char
)是一组至少(可能超过)8位。
没有。他们并不等同。我不认为你会在Java中找到一个等价的类型;他们都是固定宽度。您可以安全地使用Java中的unsigned char
作为C中byte
的等效项(但除了int8_t
之外不需要存在int8_t
除外)。
至于陷阱,你的C代码中也有一些。假设CHAR_BIT == 8
在data[0]
的任何系统上都是unsigned char
,data[0] << 24
is undefined behaviour。