【问题标题】:Run-time bit copy (bit-masking) in C++C++ 中的运行时位复制(位掩码)
【发布时间】:2018-04-10 09:30:46
【问题描述】:

我手头有一个问题并以一种方式解决了它,但我不满意我是如何解决它的,因为它并不适用于所有情况。解决方案必须在 C++(11) 中。

我有一个 char 数组和一个 int。给定相对于数据的位偏移和长度(以位为单位)。我想从数组中提取偏移量到偏移量+长度的位并将它们存储出来。

char8_t data[8];
int32_t out;
int32_t offset;
int32_t length;

Figureoffset=24; length=4;

偏移量和长度都只在运行时可用。因此,我想避免创建位掩码。我个人通过将完整数组转换为 int64_t 然后右移 (64-offset-length) 和左移 (64-length) 来解决它。

out = (*(int64_t*)data) >> (64-offset-length) << (64-length);

问题:如果我的数组更长,就没有原语来捕获完整的数组。我的解决方案不再有效。有没有更好的(缩放)方法来做到这一点?

在理想世界中,我可以创建一个带有一点偏移的指针,但这是 C++ 而不是理想世界。

替代方案我想过:通过遍历数组和左移来将 += 加到“out”上。 相当不雅!

我知道存在类似的问题,但要么回答得不好,要么答案对性能有很大影响。

【问题讨论】:

  • 不需要从头开始转换数组。添加一些初始偏移量。 reinterpret_cast&lt;uint64_t const *&gt;(data + (offset / 8))
  • 如果我有一个 uint64_t 作为目标并且offset%8 != 0 仍然会出现同样的问题。而且我需要检查我是否没有创建一个“比数组长”的 int。
  • 没有问题。在这种情况下,您需要获取uint64_t 加一个字节。
  • @VTT 你会得到未对齐的访问,这是个大问题。
  • @PasserBy 如果未对齐的访问是一个问题,则可以调整偏移量以正确对齐。主要思想是无需将整个 data 块存储在单个整数变量中,仅获取覆盖所需范围的两个整数就足够了。

标签: c++ arrays bitmask


【解决方案1】:

首先,您的方法将取决于字节序,即系统是否将最高有效字节存储在相应 8 字节内存块的开头或结尾。

其次,我会使用无符号数据类型,例如uchar8_t data[8]uint32_t 以便正确处理位移和(自动)类型提升。

如果您确切知道data[8] 中某个特定信息的存储位置以及存储顺序,则可以如下编写:

uint32_t out = data[0] + 256*data[1]; 
...

因此,您的“解码器”将被收紧到原始数据的顺序/含义;您的 data 可能会比最大的整数数据类型更长;并且您可以避免将有符号整数值移动到有符号位上可能引入的未定义行为。

如果您的偏移量不是 8 的倍数,即“值”不是从字节的开头开始,您仍然可以使用位移操作来纠正这个问题。假设该值从 2 位的偏移量开始;然后你可以写:

uint32_t out = (data[0] >> 2) + (data[1] << 6) + (data[2] << (6+8))

但是 - 最后 - 你的目标将被限制为特定数量的位,因为你特定平台上的 C 语言将保证每个原始数据类型的特定大小,unsigned long long 可能仍然是 64 位.这个限制是实现定义的,标准保证每种数据类型的最小位。这个限制是来自寄存器还是其他什么,你无法知道 - 它的实现已定义。

【讨论】:

  • 感谢您的想法。其实这一切都没有签名。所以uint,uchar等(我忘了)。 256 是“转移”数据的好选择。如果我有一个 uint64_t 作为目标和offset%8 != 0; length=64;,我仍然会遇到问题。这是寄存器最多为 64 位的固有问题吗?
【解决方案2】:

你试过std::vector&lt;bool&gt;吗? 它是std::vector 的特化,结合了向量的动态大小和std::bitset 的紧凑性。

【讨论】:

  • 这也是一个不应该被称为vector的容器的可怕误称,因为它与真正的vectors有一些重大差异......虽然我不知道它是否会很快就会从标准中删除,我想知道推荐人们开始依赖它是否不是一件好事。
  • 该讨论已经存在了好几年,并没有导致此类 AFIAK 被弃用。还是有?
【解决方案3】:

我会使用 bitset 作为临时的。先复制循环对齐的字节,再进行位对齐。

unsigned startbit = offset;
unsigned startbyte = startbit / 8;
unsigned endbit = offset + length - 1;
unsigned endbyte = endbit / 8;

bitset<8*(sizeof(out) + 1)> align(0);
for(unsigned byte = endbyte; byte >= startbyte; --byte) { // byte align copy
// for(unsigned byte = startbyte; byte <= endbyte; ++byte) { // check endianess
    align <<= 8;
    align |= data[byte];
}
align >>= startbit % 8; // bit align
align &= ((1 << length) - 1); // mask

out = align.to_ullong();

【讨论】:

    【解决方案4】:

    我使用std::bitsetboost::dynamic_bitset 来表示二进制数据并对其进行操作。如果长度固定,std::bitset 效果很好,否则boost::dynamic_bitset 是一个不错的选择。有了这个,您可以使用重载的位运算符提取位:

    #include <boost/dynamic_bitset.hpp>
    
    using boost::dynamic_bitset;
    
    dynamic_bitset<unsigned char> extract(unsigned char* first, unsigned char* last, int offset, int length) {
       dynamic_bitset<unsigned char> bits(first, last);
    
       bits >>= bits.size() - (offset  + length);
       bits.resize(length);
    
       return bits;
    }
    

    因此,您可以使用dynamic_bitset&lt;&gt; 以有效的方式保存任意位长度的值,而不是int32_t out;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-23
      • 2021-10-05
      • 1970-01-01
      • 1970-01-01
      • 2012-07-21
      • 2023-03-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多