[英]What does 0xff <<n do in Java?
从 Inputstream ,我读取前 4 个字节并将它们打包以获得一些信息,在这种情况下是流的大小/长度。
为此,我使用从另一个项目复制的跟随代码(示例 1)
示例 1:使用字节数组,其中的值从 InputStream 读取到名为in_buf[]且长度为 4 且值为 {0,0,12,26} 的数组中。
示例 1
int size = (((in_buf[0] & 0xff) << 24) | ((in_buf[1] & 0xff) << 16) |
((in_buf[2] & 0xff) << 8) | (in_buf[3] & 0xff)); // result its 3098
结果我变成了大小的值,很好,但是..
我需要解释这里发生了什么,我尝试拆分所有函数以更好地查看发生的情况并进行调试,然后我变成了以下结果
int byte1 = ((in_buf[0] & 0xff) << 24); // result 0
int byte2 = ((in_buf[1] & 0xff) << 16); // result 0
int byte3 = ((in_buf[2] & 0xff) << 8); // result 3072
int byte4 = (in_buf[3] & 0xff); // result 26
然后我从示例 1 中推断出大小的结果是 0+0+3072+26 的总和,但是这里究竟发生了什么(只有值 12 和 26)? 或者它做了哪个操作?
像这样的东西?
0000 0000 0000 1100 //12 << 8
0000 1100 0000 0000 // result after << 8
为什么我们需要使用掩码& 0xff ?
因为当您调试 int byte3a = (in_buf[3] & 0xff) 时,结果与 int byte3b = in_buf[3]; 相同; 所以 12 , *其中 in_buf[3]=12 的值; 我从调试结果中添加了一个图像。
会发生什么或使这个in_buf[3] & 0xff像这样?
0000 0000 0000 1100 (12)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 1111 0011 ?
像这样的东西?
是的,除了操作数经过数字提升并变成int
,所以从技术上讲,您应该显示 32 位。
为什么我们需要使用掩码
& 0xff
?
这就是我们将负字节视为正ints
,本质上是toUnsignedInt
所做的。 它对非负字节没有任何作用,但对于负字节,例如-1
:
1111 1111
当由于数字提升而将其转换为int
,它将被符号扩展为 32 位。 也就是说,符号将保持为负。 基本上这意味着如果数字是负数,我们填充 1,否则我们填充 0。 所以-1
变成:
1111 1111 1111 1111 1111 1111 1111 1111
现在,如果您使用<< 8
,它将是:
1111 1111 1111 1111 1111 1111 0000 0000
这是-256
。 现在让我们看看如果在<< 8
之前执行& 0xff
会发生什么。 数字提升发生了,并且像以前一样将您的字节转换为 32 个,但是& 0xff
仅获得 8 个最低有效位! 所以 int 现在变成:
0000 0000 0000 0000 0000 0000 1111 1111
这就是你如何获得原始字节,用 0 填充。 然后<< 8
做了显而易见的事情:
0000 0000 0000 0000 1111 1111 0000 0000
示例 1 中显示的代码采用数组的前四个值(其中可能包含您所描述的“输入流”或其值)。 它将它们排列成一个单一的变量,大概是一个足够宽度的(无符号)整数。
根据您未指定的协议,这样做是为了获得前四个值的大概语义含义。
那么,为什么要以这种“迂回”的方式使用这些运算符呢?
& 0xff
的使用确保仅使用低 8 位。
这是多余的,即不需要,以防您可以依赖来自字节的数组中的值,因此只包含最多位 7 的值。没有考虑任何更高的位(值 256、512,...)。 如果您不相信从流中检索单个字节到数组中,这很有意义。
但是在 Java 中(正如哈罗德在评论中亲切指出的那样)
& 0xff
在 Java 中不是多余的,因为 byte 是有符号类型,因此在转换为 int 时进行了符号扩展,并且需要删除符号的那些额外副本。 如果字节是无符号的,它将节省大量这样的代码。
使用<< N
,其中N
是 8 的倍数,将八个“收获”位移动到结果值内的位置,在那里它们对结果值有正确的影响。 其中一个字节(我故意不在这里使用“第一个”)属于最低值的位置,它没有移位,它的值按原样使用。 需要一个字节作为下一个更高值的字节,即表示 256 (0x100) 的倍数。 等等。
使用|
组装正确定位的零件。
现在为什么要以这种复杂的方式呢? 显而易见的替代方法是仅删除第 7 位以外的所有位,然后将该批次读取为一个 4 字节整数。
原因是对字节序的怀疑。 您可能不相信在内存中以特定顺序排列四个字节,如果读取为一个 4 字节值,将被解释为您在环境中的目标值。 这是因为不同的环境可能对最低地址的字节是代表最低值还是最高值有不同的看法。
所示代码强制选定字节为高值,一个为低值,其他为介于两者之间。
它将强制将该值转换为 8 位字节。
由于 Java 没有无符号类型,字节值0xFF
被解释为-1
并且由于 Java 的性质,它将被提升为 32 位 int,即0xFFFFFFFF
。
位掩码旨在为负值丢弃那些额外的位,以便它们在使用 OR 按位运算符时不会覆盖这些位|
.
这是一个字节值为255
的示例,尽管解释为-1
并提升为int
1111 1111 1111 1111 1111 1111 1111 (-1, but was originally an unsigned byte of 255)
& 0000 0000 0000 0000 0000 1111 1111 (0xff)
----------------------------------
0000 0000 0000 0000 0000 1111 1111 (an int with the original value of 255)
这些是二元运算。
究竟会发生什么?
& 0xFF
基本上意味着,你取最后一个字节。
二进制和 (&) 表示,仅当 1 在同一位置的两个数字中时,结果位置才会为 1(否则为 0)。
还有一个例子:
0000 0001 0000 1100 (268)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 0000 1100 (12)
12 会发生什么?
0000 0000 0000 1100 (12)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 0000 1100 (12)
转移:
左移只是将位向左推。
0000 0000 0000 1100 (12)
<< 8
-------------------
0000 1100 0000 0000 (3072)
26 会发生什么?
0000 0000 0001 1010 (26)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 0001 1010 (26)
为什么我们需要使用掩码 & 0xff ?
在这种情况下,你不会。 但是,如果您使用更大的数字(或负数),则可能需要使用掩码。 此外,您要确保没有多余的杂乱部分。
将所有内容与 or 放在一起:
是的,它基本上是加法,但仅限于这种情况。 实际发生的是,您将使用& 0xFF
创建的字节放在一起以创建一个数字。 这就是您以 8 的倍数进行移位的原因 - 每个字节都有自己的大小为 8 的位置。
假设您有字节 X、Y、Z、W。 然后(X<<24)|(Y<<16)|(Z<<8)|W
将创建以二进制形式构造的数字,如下所示: XYZW
。 注意,它不一定是这些数字的总和。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.