【问题标题】:Read bits from memory c++, shift and store从内存c ++中读取位,移位和存储
【发布时间】:2018-07-01 00:55:36
【问题描述】:

我希望从内存中读取 16 位,将它们向任意方向移动 n 次,然后保存回相同的内存位置..

假设在内存位置0xfffaaa 我有: 0101111101100001 我想向右移动一次 (>>1) 所以我声明了一个指向0xfffaaauint16_t 变量。

转换后,该变量包含: 0110000101011111(十进制为 24417)。 右移后: 0011000010101111 1 --> 丢失位。 存储在内存中的是: 1010111100110000 而不是: 0010111110110000 1---> 丢失位。

这里是我尝试做的一个例子..

#include <bitset>
#include <iostream>
using namespace std;
int main(){
    volatile uint8_t *bitmap = (uint8_t *)malloc(2);
    bitmap[0] = 0b01011111;
    bitmap[1] = 0b01100001;

    cout << bitset<8>(bitmap[0]);
    cout << bitset<8>(bitmap[1]) << '\n' << '\n';

    uint16_t* p16 = (uint16_t*)bitmap;
    cout << bitset<16>(p16[0]) << '\n';
    p16[0]>>=1;
    cout << bitset<16>(p16[0]) << '\n' << '\n';

    cout << bitset<8>(bitmap[0]);
    cout << bitset<8>(bitmap[1]) << '\n';
   return 0;
}

控制台输出为:

0101111101100001

0110000101011111
0011000010101111

1010111100110000

如何以我希望使用 uint16_t 的方式进行位移?

【问题讨论】:

  • 这在 little-endian 机器上是正常的。是否需要通过保持位图的当前格式来修复它,还是可以调整其字节顺序?
  • 我需要保留位图格式。我可以做到这一点,它适用于 uitn16_t 的 uitn8_t,但我需要提高速度,减少操作次数,所以我尝试这种方式(显然我对 uint32_t 和 uint64_t 有同样的问题)。

标签: c++ memory bit-manipulation bit-shift


【解决方案1】:

问题不在于移位,而在于如何从位图中获取数据。

由于右移会将位从 bitmap[0] 移动到 bitmap[1],因此您可以通过乘法将数据提取为更大的类型:

auto v16 = 256 * bitmap[0] | bitmap[1];
v16 >>= 1;
bitmap[0] = uint8_t(v16 / 256);
bitmap[1] = uint8_t(v16 & 255);

或者,仅使用移位操作:

auto v16 = (bitmap[0] << 8) + bitmap[1];
v16 >>= 1;
bitmap[0] = uint8_t(v16 >> 8);
bitmap[1] = uint8_t(v16 & 0xFF);

【讨论】:

  • 这个想法还不错,但是在 C++ 中你有原生的移位运算符,你不需要使用乘法和除法来创建移位。
  • @BenVoigt 我添加了一个仅换档的替代方案
  • 最后一个偷偷摸摸的算术运算符幸存下来——按位或比将字节组装成更大的整数变量要好。
  • 非常感谢您的回答。我没有意识到我可以在那种模式下做到这一点。第二个选项更适合我的需要,我可以扩展它以转移更大的缓冲区。我认为,有一种形式可以从内存中读取普通位并移动它们以减少处理时间。
【解决方案2】:

要知道它是否确实更快,您必须运行分析器。

有两种方法可以解决您的问题(如果您不包括在位图中交换数据的可能性。)

  1. 就像@1201ProgramAlarm 所说:

    // 获取该指针 uint16_t* p16 = (uint16_t*)位图; uint16_t v(*p16); v = (v >> 8) | (v >= 1; *p16 = (v >> 8) | (v

这可以用几种不同的方式编写,您甚至可以将转换合并到交换中:

v = (v >> 9) | (v << 7);
  1. 您没有使用 p16 指针

既然你可以同时进行swap和shift,你也可以直接使用你的p8:

uint61_t v((bitmap[1] >> 9) | (bitmap[0] << 7));
bitmap[0] = v >> 8;
bitmap[1] = v; // C auto-and (i.e. (v & 0xFF) is not required)

请注意,您无法进行更多优化,因为当您修改位图时,您无法在修改后重新加载数据。小心那个。

现在,汇编中的 this 会像这样进行优化,尽管它可能不是最快的(当您让 C/C++ 编译器进行优化工作时,有时会有惊喜!)

mov ax, [ep]  ; get current value (16 bits)
xchg al, ah
shr ax, 1     ; shift by one unsigned
xchg al, ah
mov [ep], ax  ; save result

对于 32 位和 64 位,x86 中有一条 swapb 指令(64 位也是如此)。

请注意,一次读取和写入一个字节可能与读取 16 位和xchg-ing 一样快。

【讨论】:

    【解决方案3】:

    你可以有效地使用联合:

    #include <bitset>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    union joinbits
    {
        uint16_t data;
        uint8_t  bits[2];
    };
    
    int main(){
        volatile uint8_t *bitmap = (uint8_t *)malloc(2);
    
        bitmap[0] = 0b01011111;
        bitmap[1] = 0b01100001;
    
        cout << bitset<8>(bitmap[0]);
        cout << bitset<8>(bitmap[1]) << '\n' << '\n';
    
        joinbits j;
        j.bits[1] =  bitmap[0];
        j.bits[0] =  bitmap[1];
    
        j.data >>= 1;
    
        bitmap[0] = j.bits[1];
        bitmap[1] = j.bits[0];
    
        cout << bitset<8>(bitmap[0]);
        cout << bitset<8>(bitmap[1]) << '\n';
    
        return 0;
    }
    

    输出是:

    0101111101100001                                                                                                                                               
    
    0010111110110000                                                                                                                                               
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-10
      • 2018-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-11
      相关资源
      最近更新 更多