C++如何使用指向循环移位数组元素的指针

C++ how to use a pointer to circular shift array element

#include <iostream>
using namespace std;

void RotateLeft(unsigned char* in)
{
    unsigned int* q= (unsigned int*)in;
    *q = (*q >> 8)|((*q & 0xff) << 24);
}
int main() {
    unsigned char temp[4] = {'a', 'b', 'c', 'd'};
    RotateLeft(temp);
    for (int i=0; i<4; i++) {
        cout<<temp[i]<<endl;
    }
}

输出为:b c d a.

你能解释一下这条线是如何工作的吗:

*q = (*q >> 8)|((*q & 0xff) << 24);

?

它只是引用指针,returns 和 int,并对其执行所有位操作。它实际上等同于:

unsigned int val = *q;
val = (val >> 8)|((val & 0xff) << 24);
*q = val;

程序本身有未定义的行为。您不能通过 unsigned int* 指针访问 char 数组。它还假定 sizeof (unsigned int)4,数组在元素之间没有填充,并且 CPU.

具有特定的字节顺序

这怎么行?

指向数组的指针 in 如下所示:

         +---+---+---+---+
in -->   | a | b | c | d | 
         +---+---+---+---+

让我们假设每个字符都被编码为 8 位序列(好的,语言律师会争辩说一个字节不一定是 8 位,但实际上,情况经常如此)。所以在二进制中它看起来像:

         +----------+----------+----------+----------+
in -->   | 01100001 | 01100010 | 01100011 | 01100100 | 
         +----------+----------+----------+----------+

假设 int 由 32 位组成。无符号 int* q= (unsigned int*)in; 的丑陋 casting 技巧是指示编译器处理指针,就好像它指向 int,将多个字符组合成一个值:

         +----------+----------+----------+----------+
q  -->   | 01100001   01100010   01100011   01100100 | 
         +----------+----------+----------+----------+
   

注意:为了简单起见,我将在这里假设一个大端 CPU 体系结构。但稍后我会解释它如何与小端一起工作。
示例中的这个二进制编码数字表示十进制表示法中的 1633837924。

如果您现在执行 (*q >> 8),它将 shift the bits 这个整数向右移动 8 位,在左侧注入 0 位:

         +----------+----------+----------+----------+
*q >>8 = | 00000000   01100001   01100010   01100011 | 
         +----------+----------+----------+----------+
                        'a'         'b'        'c'

现在 0xff 在二进制 11111111 中。如果您现在执行以下 bitwise and 操作 (*q & 0xff) 您会将所有位设置为 0,最后 8 位除外:

            +----------+----------+----------+----------+
(*q&0xff) = | 00000000   00000000   00000000   01100100 | 
            +----------+----------+----------+----------+
                                                 'd'

如果将其与 ...<<24 结合使用,则通过向右注入 0,将所有位向左移动 24 个位置:

                +----------+----------+----------+----------+
(*q&0xff)<<24 = | 01100100   00000000   00000000   00000000 | 
                +----------+----------+----------+----------+
                     'd'

如果您现在将这两项与 bitwise or 结合起来,您将获得:

                +----------+----------+----------+----------+
*q >>8 =        | 00000000   01100001   01100010   01100011 | 
                +----------+----------+----------+----------+
                             'a'         'b'        'c'
                +----------+----------+----------+----------+
(*q&0xff)<<24 = | 01100100   00000000   00000000   00000000 | 
                +----------+----------+----------+----------+
                     'd'
                +----------+----------+----------+----------+
| (bitwise or)  | 01100100   01100001   01100010   01100011 | 
                +----------+----------+----------+----------+
                     'd'        'a'         'b'        'c'

所以在这种情况下它向右旋转。这是我所做的大端假设的结果。

但这安全吗?

此代码的问题在于它假设了很多标准 C++ 无法保证的事情。所以不能保证有效。它仅在以下情况下有效:

  • a char是8位长(因为右移和左移是8的倍数)。
  • int 是 32 位长(因为它假设移位的组合恰好对应于 32 位)。
  • 旋转方向取决于字节顺序。只有小端确保向左旋转。

此处对endianness的影响:

  • 在我的逐步解释中,我使用了大端法,其中字节以正确的顺序连续获取以形成整数。向右旋转。
  • 如果你有一个小端架构,这更有可能,字节将以相反的顺序作为整数加载到寄存器中(例如,内存顺序中的 a b c d 将加载为 d c b a 用于计算),然后字节将按照右侧的说明进行移位和组合(例如 a d c b),但是当存储回内存时,字节将再次反转(例如 b c d a ),因此如果查看单个字符,则对整数进行的向右旋转会导致向左旋转。