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

时间:2017-11-15 20:21:45

标签: c++ arrays pointers rotation shift

#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);

2 个答案:

答案 0 :(得分:3)

它只是引用指针,它返回一个int,并对其执行所有位操作。它实际上与:

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

程序本身具有未定义的行为。您无法通过char指针访问unsigned int*数组。它还假设sizeof (unsigned int)4,数组之间没有填充,以及CPU的特定字节序。

答案 1 :(得分:2)

这怎么办?

数组的指针in如下所示:

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

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

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

假设int由32位组成。使用unsigned int* q= (unsigned int*)in;的丑陋casting技巧是指示编译器处理指针,就像指向int一样,在单个值中组合多个char:

         +----------+----------+----------+----------+
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结合使用,则将所有位向左移动24个位置,方法是向右侧注入0:

                +----------+----------+----------+----------+
(*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的影响:

  • 在我的逐步解释中,我使用了big-endian,其中字节按正确的顺序依次采用以形成整数。向右旋转是完成的。
  • 如果你有一个更有可能的小端架构,那么字节将以相反的顺序作为整数加载到寄存器中(例如,内存顺序中的a b c d将被加载为{{1}对于计算而言,然后将字节移位并组合,如右边所解释的那样(例如d c b a),但是当存储回存储器时,字节将再次被反转(例如a d c b),因此,如果查看各个字符,则对整数执行的向右旋转将导致向左旋转。