c - 以 32/64 位数量有效地移位字节?

标签 c bit-manipulation bit-shift

为简单起见,假设我使用的是 32 位小端处理器并声明了以下 4 字节缓冲区:

unsigned char buffer[] = { 0xab, 0xcd, 0xef, 0x46 };

假设我的目标是将缓冲区中的每个字节按位左移 4 位。也就是说,我想将缓冲区值转换为: { 0xbc、0xde、0xf4、0x60 }。要执行这样的转换,可以编写如下代码:

for (int i = 0; i < 3; ++i)
{
  buffer[i] <<= 4; 
  buffer[i] |= (buffer[i + 1] >> 4);
}
buffer[3] <<= 4;

虽然这可行,但我更愿意使用处理器的 native 32 位寄存器同时移动所有 4 个字节:

unsigned char buffer[] = { 0xab, 0xcd, 0xef, 0x46 };
unsigned int *p = (unsigned int*)buffer; // unsigned int is 32 bit on my platform
*p <<= 4;

上面的代码片段成功地执行了一次转换,但不是我正在寻找的方式。看来,由于我将缓冲区转换为无符号整数,寄存器加载(小端)值 0x46efcdab(而不是 0xabcdef46)。因此,执行 4 位左移会导致 0xb0dafc6e 而不是 0xbcdef460

除了在移位前交换字节(例如 htonl 等)之外,还有什么技巧可以按照我想要的方式有效地移位字节吗?

预先感谢您的见解。

最佳答案

使用htonl/ntohlnetwork(big-endian)字节顺序和native 字节顺序之间切换:

uint32_t *p = (uint32_t*)buffer;
*p = htonl(ntohl(*p) << 4);

实际上,这会将缓冲区内容作为整数以大端顺序加载,执行移位,然后以大端顺序将其写回。

这会在 x86 上编译成几个 bswap 指令,因此它应该相当高效 (gcc -O3)。


下面是一些测试代码(buffer 是全局的以避免常量折叠,return 防止死代码消除):

#include <stdint.h>    // uint32_t
#include <arpa/inet.h> // ntohl, htonl

unsigned char buffer[] = { 0xab, 0xcd, 0xef, 0x46 };

int main() {
    uint32_t *p = (uint32_t*)buffer; // unsigned int is 32 bit on my platform
    *p = htonl(ntohl(*p) << 4);
    return *p;
}

这会编译成以下相当简单的机器代码(x86-64;LLVM 7.0.2;cc -O2):

0000000000000000    pushq   %rbp           ; frame setup
0000000000000001    movq    %rsp, %rbp     ; frame setup
0000000000000004    movl    (%rip), %eax   ; load buffer
000000000000000a    bswapl  %eax           ; endian flip
000000000000000c    shll    $0x4, %eax     ; shift
000000000000000f    bswapl  %eax           ; endian flip
0000000000000011    movl    %eax, (%rip)   ; save buffer
0000000000000017    popq    %rbp           ; finish
0000000000000018    retq

关于c - 以 32/64 位数量有效地移位字节?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37083879/

相关文章:

c++ - 使用 Boost 计算 ECMA-128 64 位 CRC

c - 逻辑算术移位

c - 为什么一个代码示例比其他代码示例需要更多的时间来执行?

Python 从 float 中获取最低有效数字(不使用字符串操作)

c++ - 有没有更快的方法在 SIMD 上乘以 2(不使用乘法)?

c - 将 n 字节整数拆分为单个字节的可移植方法

c++ - 按位整数连接

c - 了解从 C 到 MatLab 的位移错误

C:自动存储类数组?

c - 在什么条件下管道读取是原子的?