【问题标题】:C++ how to use a pointer to circular shift array elementC ++如何使用指向循环移位数组元素的指针
【发布时间】:2017-11-15 20:21:45
【问题描述】:
#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);

?

【问题讨论】:

    标签: c++ arrays pointers rotation shift


    【解决方案1】:

    它只是推迟返回int 的指针,并对其执行所有位操作。它实际上与以下内容相同:

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

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

    【讨论】:

      【解决方案2】:

      这是怎么工作的?

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

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

      假设每个 char 将被编码为 8 位序列(好吧,语言律师会争辩说一个字节不一定是 8 位,但在实践中,情况往往如此)。所以在二进制中它看起来像:

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

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

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

      注意:为了简单起见,我假设这里是一个大端 CPU 架构。但稍后我将通过解释它如何与 little endian 一起使用来回过头来解释它。
      示例中的这个二进制编码数字以十进制表示 1633837924。

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

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

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

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

      如果将其与...&lt;&lt;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++ 无法保证的东西。所以不能保证能正常工作。它仅在以下情况下有效:

      • char 的长度为 8 位(因为右移和左移都是 8 的倍数)。
      • int 的长度为 32 位(因为它假定移位的组合正好对应于 32 位)。
      • 旋转方向取决于字节顺序。只有 little endian 才能确保向左旋转。

      这里对endianness的影响:

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

      【讨论】:

      • 非常感谢您提供如此详细的解释。非常清楚!关于‘(*q >> 8) | 再问一个问题。 ((*q & 0xff) > 8)' 操作?
      • @Jason 因为这些移位操作是在 *q 的值上执行的,除非有赋值,否则不会更改对象的原始内容。就像写a+15;你也不希望 a 改变。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-02
      • 2016-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-22
      相关资源
      最近更新 更多