位向量操作和Endianness

时间:2014-04-23 19:42:50

标签: c bit-manipulation endianness bitvector

我在我的软件中做了很多位向量操作。例如:假设我需要存储有关候选'n'的布尔信息,我会执行以下操作:

uint64_t *information_vector;
uint32_t pos = n / 64;
uint32_t bit_pos = n % 64;

information_vector[pos] |= (1 << bit_pos);

我在阅读该信息时遵循类似的程序:

uint32_t pos = n / 64;
uint32_t bit_pos = n % 64;
if (information_vector[pos] & (1 << bit_pos)) {
       // do something
}

与此同时,我还将information_vector写入磁盘并再次读回。现在,我正试图解决一个让我做恶梦的错误,让我感到Endianess可能是罪魁祸首,但我无法解释。有什么方法可以检查吗?这个位向量操作通常是endian安全吗?并且跨架构?

我也看到代码中的某个地方我在另一个位向量中为同一个候选者设置了一些其他信息:

uint8_t byte_position = n / 8;
uint8_t bit_position = n % 8;
another_information_vector[byte_position] |= (1 << bit_position);

我通常通过这些位向量找到一组共同的属性。

3 个答案:

答案 0 :(得分:1)

一般来说,如果你总是使用相同的类型(在你的情况下uint64_t)访问你的位向量,并且你访问数据的所有系统的字节序是相同的,那么Endian-ness不会成为问题。

让自己放心的最简单方法是将对象的地址强制转换为char*并取消引用,这样可以按照内存中的顺序一次看到一个字节。

更新:我刚观察到您的第三个代码块似乎通过执行byte_position来计算n % 8

如果您有时写出uint64_t数组,有时将其视为uint8_t数组,那么如果您的系统是小端,则结果可能会出乎意料。

避免此问题的最佳方法是保持类型一致。

To make this problem more concrete, consider the following example:

#include <stdio.h>
#include <stdint.h>

int main(){
    uint64_t myVector = 1 << 2; // set second bit of LSB
    uint8_t * ptr = (uint8_t *) &myVector;
    int i;
    for (i = 0; i < 8; i++)
       printf("%x\n", ptr[i]);
}

在我的小端x86系统上,这将打印4后跟7 0,因为最高有效字节存储在最高地址的地址中uint64_t。如果您习惯于从左到右考虑从最高有效到最低有效的位,这可能与您的直觉背道而驰。

答案 1 :(得分:1)

这肯定是CPU内部架构的endian安全。从一个体系结构写入磁盘然后在不同的体系结构上读取它将取决于您如何读取和写入磁盘。这与将任何多字节数写入磁盘并将其读回时所遇到的问题没有什么不同。两端都必须解释这个数字。如果在这个例子中你只是将8个字节写入磁盘,然后在不同的endian架构上读取它们,那么你将会交换字节。

答案 2 :(得分:1)

对于大多数情况,最安全的变体是在字节级操作,因此,除数为8. OTOH在某些情况下可能不是最理想的。与单词访问相比,有些架构无法直接访问字节,或者访问费用昂贵。

在小端机器上,选择任何合理的除数(8,16,32,64)时,相同的方法不会改变。例如,对于位索引22,字节级访问处理具有索引2的字节的第6位;短字访问处理短字的第6位为1;等等。

在big-endian机器上,这需要将1 << bit_position替换为1 << (BITS_PER_CELL-1-bit_position)或(相同)HIGHEST_BIT >> bit_position,其中HIGHint_BIT对于uint8_t为0x80,对于uin32_t为0x80000000等。并且,位索引0将表示字节0的MSB,而不是小端情况,其中它表示字节0的LSB。

(在串行线路上可以看到类似的效果。在RS232或以太网中,字节从LSB传输到MSB.MAC地址中的个别/组位是线路上的第一个,但是它的LSB第一个八位字节。)