Bitshift -需要解释才能理解代码

Bitshift - Need explanation to understand the code

本文关键字:能理解 代码 解释 Bitshift      更新时间:2023-10-16

我想知道这个函数实际执行什么。根据我的理解,它应该返回pSrc[1]。

那么为什么要把pSrc[0]左移8位,这8位就归零了呢?当这些零与pSrc[1]或时,pSrc[1]不受影响,因此无论如何您都会得到pSrc[1],就像从未发生过按位或一样。

/*
* Get 2 big-endian bytes.
*/
INLINE u2 get2BE(unsigned char const* pSrc)
{
    return (pSrc[0] << 8) | pSrc[1];
}

这个函数来自dalvik虚拟机的源代码。https://android.googlesource.com/platform/dalvik/+/android-4.4.4_r1/vm/Bits.h

更新:

好了,多亏了这里所有的答案,现在我明白了。

(1) pSrc[0]本来是一个unsigned char(1字节)。

(2)左移时(pSrc[0] <<8)对于int类型的字面值8,pSrc[0]因此被int提升为有符号int(4字节)。

(3) pSrc[0] <8表示pSrc[0]中感兴趣的8位被移到有符号整型的4个字节的第二个字节,从而在其他字节(第1、3和4个字节)中留下零。

(4)当它被oror(步骤(3)的中间结果| pSrc[1])时,pSrc[1]被int型提升为有符号int型(4字节)。

(5)(步骤(3)的中间结果| pSrc[1])将前两个最低有效字节保留为0,这两个最高有效字节都为0。

(6)通过返回u2类型的结果,只返回前两个最低有效字节来获得2个大端字节。

对于这样的算术运算,unsigned char通过一个称为积分提升的过程进行转换

c++ 11 - N3485§5.8 [expr.shift]/1:

操作数必须为整型或无作用域枚举类型,并执行整型提升。操作结果的类型为提升后的左操作数的类型。

和§13.6 [over.built]/17:

对于每一对提升整型L和R,存在形式为

的候选算子函数
LR operator%(L , R );
LR operator&(L , R );
LR operator^(L , R );
LR operator|(L , R );
L operator<<(L , R );
L operator>>(L , R );

其中LR是类型L和r之间通常的算术转换的结果。

当完成积分提升时(§4.5 [conv.prom]/1):

非bool、char16_t、char32_t或wchar_t的整型右值如果int可以表示所有,则可以将其转换为int类型的右值源类型的值;否则,源右值可以转换为unsigned类型的右值int。

通过积分提升,unsigned char将被提升到int。另一个操作数已经是int了,所以没有对它的类型做任何改变。返回类型也变为int

因此,你得到的是第一个unsigned char的位左移,但仍然在现在更大的int中,然后是第二个unsigned char的位在最后。

您会注意到operator|的返回类型是两个操作数之间常规算术转换的结果。此时,这些是来自移位的int和第二个unsigned char

这个转换定义如下(§5 [expr]/10):

许多要求操作数为算术或枚举类型的二元操作符会导致转换并产生结果结果以类似的方式类型。目的是产生一个公共类型,这也是结果的类型。这种模式称为常规算术转换,定义如下:

…否则,将对两个操作数进行积分提升(4.5)。接下来是规则适用于提升的操作数:

…如果两个操作数具有相同的类型,则不需要进一步的转换。

由于在此之前被提升的LR已经是int,因此提升后它们保持不变,因此表达式的整体返回类型为int,然后将其转换为u2,无论发生什么。

除类型转换外,没有其他操作unsigned char。任何操作前,积分推广发生,将unsigned char转换为int。因此,操作是将int向左移动,而不是将unsigned char向左移动。

C11 6.5.7位移位运算符

在每个操作数上执行整数提升。类型结果为提升后的左操作数。如果右操作数为负或大于等于提升左操作数的宽度,行为未定义。

E1 <<E2为E1左移E2位的位置;空出的位被填满0。如果E1为无符号类型,则结果为E1 × 2E2,取约模比结果类型中可表示的最大值多一个。如果E1有带符号的类型和非负值,且E1 × 2E2在结果类型中可表示,则为结果值;

所以pSrc[0]是整型提升为int。字面量8已经是一个int,因此不会进行整数提升。通常的算术转换不适用于移位运算符:它们是一种特殊情况。

由于原始变量是左移8位的unsigned char,我们还遇到了"E1"(我们提升的变量)有符号的问题,并且结果可能无法在结果类型中表示,如果这是一个16位系统,这将导致未定义的行为。

用简单的英语来说:如果你把一些东西移到一个有符号变量的符号位上,任何事情都可能发生。一般来说:依赖隐式类型提升是糟糕的编程和危险的做法。

你应该这样修改代码:

((unsigned int)pSrc[0] << 8) | (unsigned int)pSrc[1]