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

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

本文关键字:循环移位 数组元素 指针 何使用 C++      更新时间:2023-10-16
#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);

它只是延迟返回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;的丑陋的强制转换技巧是指示编译器处理指针,就好像它指向int一样,将几个字符组合在一个值中:

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

注意:为了简单起见,我在这里假设使用big-endian CPU架构。但我稍后会解释它是如何使用little-endian的
此示例中的二进制编码数字以十进制表示1633837924。

如果你现在执行(*q >> 8),它将把这个整数的位向右移动8位,在左边注入0位:

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

现在0xff11111111中是二进制的。如果您现在执行以下按位和操作(*q & 0xff),您将把除最后8位之外的所有位设置为0:

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

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

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

如果你现在将这两个术语与按位或组合,你将获得:

+----------+----------+----------+----------+
*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'

所以在这种情况下,它向右旋转。这是我所做的big-endian假设的结果。

但这安全吗

这个代码的问题在于,它假设了许多标准C++无法保证的东西。因此,它不能保证有效。它只在以下情况下工作:

  • char是8比特长(因为右移和左移是8的倍数)
  • CCD_ 15是32比特长(因为它假定移位的组合恰好对应于32比特)
  • 旋转的方向取决于字节序。只有小endian可以确保向左旋转

这里是对endianness的影响:

  • 在我一步一步的解释中,我使用了big-endian,其中字节按正确的顺序依次形成整数。向右旋转
  • 如果你有一个小端体系结构,这更有可能,字节将以相反的顺序作为整数加载到寄存器中(例如,内存顺序中的a b c d将作为d c b a加载用于计算),然后字节将被向右移动和组合(例如,a d c b),但当存储回内存时,字节将再次反转(例如b c d a),因此如果查看单个字符,则对整数执行的向右旋转会导致向左旋转