0xff <<n 在 Java 中做什么?
What does 0xff <<n do in Java?
从 Inputstream 中,我读取了前 4 个字节并将它们打包以获得
在这种情况下,一些信息是 Stream 的 size/len。
为此,我使用了从另一个项目复制的代码(示例 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 中推断出 size 的结果是 0+0+3072+26 的总和
但是这里到底发生了什么(只有值 12 和 26)?或者它进行了哪些操作?
是这样的吗?
0000 0000 0000 1100 //12 << 8
0000 1100 0000 0000 // result after << 8
以及为什么我们需要使用掩码 & 0xff ?
cuz 当你调试 int byte3a = (in_buf[3] & 0xff) 结果与 int byte3b = in_buf[3]; so 12, *其中 in_buf[3]=12 的值;我从我的调试结果中添加了一张图片。
发生什么或使这个 in_buf[3] & 0xff 之类的东西?
0000 0000 0000 1100 (12)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 1111 0011 ?
Screenshot from Eclipse
some thing like these ?
是的,除了操作数经过 numeric promotion 并变成 int
,所以技术上你应该显示 32 位。
and why we need use the mask & 0xff
?
这就是我们将负字节视为正字节 ints
,本质上就是 toUnsignedInt
正在做的事情。它对 non-negative 字节不执行任何操作,但对于负字节,比如 -1
:
1111 1111
当由于数字提升而转换为 int
时,它将是 sign-extended 到 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
它会将值强制转换为 8 位字节。
由于 Java 没有无符号类型,0xFF
的字节值被解释为 -1
并且由于 Java 的性质,它将被提升为32 位整数,即 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)
example1 中显示的代码采用数组的前四个值(可能包含您描述为“输入流”或其值的内容)。它将它们排列成一个变量,大概是一个足够宽度的(无符号)整数。
它这样做是为了根据您未指定的协议获得前四个值的大概语义。
那么,为什么要使用这些运算符以那种“迂回”的方式这样做呢?
使用& 0xff
确保只使用低8位。
这是多余的,即不需要,以防万一您可以依赖来自字节的数组中的值,因此只包含最多第 7 位的值。没有任何更高的位(值为 256、512,...)被考虑在内。如果您不信任从流中将单个字节检索到数组中,这是有道理的。
然而在 java(正如哈罗德在评论中亲切指出的那样)
& 0xff
is not redundant in Java because byte is a signed type, and hence sign-extended when converted to int, and those extra copies of the sign need to be removed. If byte was unsigned it would save a lot of code like this.
使用 << N
,N
是 8 的倍数,将八个“收获”的位移动到结果值内的位置,它们对结果值有正确的影响.其中一个字节(我在这里故意不使用“first”)属于 lowest-valued 位置,它没有移位,它的值按原样使用。需要一个字节作为下一个更高值的字节,即表示 256 (0x100) 的倍数。等等。
使用 |
组装正确定位的部件。
现在为什么要用这种复杂的方式呢?显而易见的替代方法是删除第 7 位之后的所有位,然后将这批数据作为一个 4 字节整数读取。
原因是对字节序的怀疑。您可能不相信在内存中以特定顺序排列四个字节,如果读取为一个 4 字节值,将被解释为您在环境中的目标值。这是因为不同的环境可能对最低地址的字节应该被认为是代表最低值还是最高值的字节有不同的看法。
显示的代码将所选字节强制为高值,一个为低值,其他为介于两者之间。
这些是二元运算。
究竟发生了什么?
& 0xFF
基本上意味着,你拿最后一个字节。
二进制和 (&) 表示,只有当 1 在两个数字中的相同位置(否则为 0)时,结果位置才会为 1。
还有一个例子:
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
。请注意,它不一定是这些数字的总和。
从 Inputstream 中,我读取了前 4 个字节并将它们打包以获得 在这种情况下,一些信息是 Stream 的 size/len。
为此,我使用了从另一个项目复制的代码(示例 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 中推断出 size 的结果是 0+0+3072+26 的总和 但是这里到底发生了什么(只有值 12 和 26)?或者它进行了哪些操作?
是这样的吗?
0000 0000 0000 1100 //12 << 8
0000 1100 0000 0000 // result after << 8
以及为什么我们需要使用掩码 & 0xff ?
cuz 当你调试 int byte3a = (in_buf[3] & 0xff) 结果与 int byte3b = in_buf[3]; so 12, *其中 in_buf[3]=12 的值;我从我的调试结果中添加了一张图片。
发生什么或使这个 in_buf[3] & 0xff 之类的东西?
0000 0000 0000 1100 (12)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 1111 0011 ?
Screenshot from Eclipse
some thing like these ?
是的,除了操作数经过 numeric promotion 并变成 int
,所以技术上你应该显示 32 位。
and why we need use the mask
& 0xff
?
这就是我们将负字节视为正字节 ints
,本质上就是 toUnsignedInt
正在做的事情。它对 non-negative 字节不执行任何操作,但对于负字节,比如 -1
:
1111 1111
当由于数字提升而转换为 int
时,它将是 sign-extended 到 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
它会将值强制转换为 8 位字节。
由于 Java 没有无符号类型,0xFF
的字节值被解释为 -1
并且由于 Java 的性质,它将被提升为32 位整数,即 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)
example1 中显示的代码采用数组的前四个值(可能包含您描述为“输入流”或其值的内容)。它将它们排列成一个变量,大概是一个足够宽度的(无符号)整数。
它这样做是为了根据您未指定的协议获得前四个值的大概语义。
那么,为什么要使用这些运算符以那种“迂回”的方式这样做呢?
使用& 0xff
确保只使用低8位。
这是多余的,即不需要,以防万一您可以依赖来自字节的数组中的值,因此只包含最多第 7 位的值。没有任何更高的位(值为 256、512,...)被考虑在内。如果您不信任从流中将单个字节检索到数组中,这是有道理的。
然而在 java(正如哈罗德在评论中亲切指出的那样)
& 0xff
is not redundant in Java because byte is a signed type, and hence sign-extended when converted to int, and those extra copies of the sign need to be removed. If byte was unsigned it would save a lot of code like this.
使用 << N
,N
是 8 的倍数,将八个“收获”的位移动到结果值内的位置,它们对结果值有正确的影响.其中一个字节(我在这里故意不使用“first”)属于 lowest-valued 位置,它没有移位,它的值按原样使用。需要一个字节作为下一个更高值的字节,即表示 256 (0x100) 的倍数。等等。
使用 |
组装正确定位的部件。
现在为什么要用这种复杂的方式呢?显而易见的替代方法是删除第 7 位之后的所有位,然后将这批数据作为一个 4 字节整数读取。
原因是对字节序的怀疑。您可能不相信在内存中以特定顺序排列四个字节,如果读取为一个 4 字节值,将被解释为您在环境中的目标值。这是因为不同的环境可能对最低地址的字节应该被认为是代表最低值还是最高值的字节有不同的看法。
显示的代码将所选字节强制为高值,一个为低值,其他为介于两者之间。
这些是二元运算。
究竟发生了什么?
& 0xFF
基本上意味着,你拿最后一个字节。
二进制和 (&) 表示,只有当 1 在两个数字中的相同位置(否则为 0)时,结果位置才会为 1。
还有一个例子:
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
。请注意,它不一定是这些数字的总和。