如何在C中读取5个字节到有意义的uint64_t?

时间:2011-05-23 08:52:13

标签: c int endianness

我需要分配一个uint64_t[1e9]数组来计算某些东西,我知道这些项目介于(0,2 ^ 39)之间。 所以我希望数组为calloc 5 * 1e9个字节。

然后我发现,如果我想使uint64_t有意义,那么很难通过字节顺序。

应该有两种方式。

首先检查字节顺序,以便我们可以{5}将5个字节{8}整个字节中的第一个或最后一个字节。

另一种方法是使用5位移位,然后将位或它们放在一起。

我认为前者应该更快。

那么,在GCC或libc或GNU系统下,是否有任何头文件来指示当前系统是Little Endian还是Big Endian?我知道x86_64是Little Endian,但我不喜欢写一个不可移植的代码。

当然欢迎任何其他想法。

添加:

我需要使用数组来计算许多使用D-left散列的字符串。我计划使用21位的密钥和18位的计数。

3 个答案:

答案 0 :(得分:1)

当你说“更快”时......这段代码的执行频率是多少? 5次<<8|可能花费不到100ns。因此,如果该代码执行10'000次,则最多可加1(一)秒。

如果代码执行次数较少,并且您需要超过1秒才能实现endian-clean解决方案,那么您就浪费了每个人的时间。

那就是说,找出结束的解决方案很简单:

int a = 1;
char * ptr = (char*)&a;
bool littleEndian = *ptr == 1;

现在您需要一台大端机器和几个测试用例来确保您的memcpy解决方案正常运行。请注意,您需要在两种情况之一中调用memcpy五次以重新排序字节。

或者你可以简单地转移或者五次......

编辑我想我误解了你的问题。您是说要使用uint64_t的最低5个字节(= 40位)作为计数器,是吗?

因此操作将被执行很多次。同样,memcpy完全没用。我们取数0x12345678(32位)。在内存中,看起来像这样:

0x12 0x34 0x56 0x78    big endian
0x78 0x56 0x34 0x12    little endian

如您所见,字节被交换。因此,要在两者之间进行转换,必须使用位移或字节交换。 memcpy不起作用。

但这并不重要,因为CPU会为你做解码。您所要做的就是将位移到正确的位置。

 key = item & 0x1FFFFF
 count = (item >>> 21)

阅读和

 item = count << 21 | key

写。现在你只需要从五个字节构建密钥就可以了:

 key = (((hash[0] << 8) | (hash[1]<<8)) | ....

编辑2

看起来你有一个40位整数的数组,你想要读/写那个数组。

我有两个解决方案:只要不在不同字节序的CPU之间复制数据,使用memcpy就可以正常工作(读取:向磁盘保存/加载数据时)。但是对于如此庞大的数组,函数调用可能太慢了。

另一个解决方案是使用两个数组:

int lower[];
unit8_t upper[]

即:将位33-40保存在 second 数组中。要读取/写入值,需要一个班次+ or

答案 1 :(得分:0)

如果将数字视为数字而不是字节数组,则代码将与endianess无关。因此,我会选择 shift 解决方案。

话虽如此,我真的没有抓住你想要做的事情?你真的需要十亿个条目,每个五个字节长吗?如果您采样的数据稀少,您可能会分配更少的内存。

答案 2 :(得分:0)

好吧,我发现内核头文件附带<asm/byteorder.h>

内联memcpy到while(i<x+3){++*i=++*j}的速度可能仍然较慢,因为缓存操作比寄存器慢。

memcpy的另一种方式是:

union dat {
 uint64_t a;
 char b[8];
} d;
相关问题