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
),因此如果查看单个字符,则对整数进行的向右旋转会导致向左旋转。
#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
),因此如果查看单个字符,则对整数进行的向右旋转会导致向左旋转。