位域和对齐

时间:2016-04-16 19:06:23

标签: c bit-fields

尝试将数据打包到数据包中。该数据包应为64位。我有这个:

typedef union {
  uint64_t raw;
  struct {
    unsigned int magic    : 8;
    unsigned int parity   : 1;
    unsigned int stype    : 8;
    unsigned int sid      : 8;
    unsigned int mlength  : 31;
    unsigned int message  : 8;
  } spacket;
} packet_t;

但似乎无法保证对齐。因为当我运行时:

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

const char *number_to_binary(uint64_t x)
{
    static char b[65];
    b[64] = '\0';

    uint64_t z;
    int w = 0;
    for (z = 1; w < 64; z <<= 1, ++w)
    {
        b[w] = ((x & z) == z) ? '1' : '0';
    }

    return b;
}

int main(void)
{
  packet_t ipacket;
  bzero(&ipacket, sizeof(packet_t));
  ipacket.spacket.magic = 255;
  printf("%s\n", number_to_binary(ipacket.raw));
  ipacket.spacket.parity = 1;
  printf("%s\n", number_to_binary(ipacket.raw));
  ipacket.spacket.stype = 255;
  printf("%s\n", number_to_binary(ipacket.raw));
  ipacket.spacket.sid = 255;
  printf("%s\n", number_to_binary(ipacket.raw));
  ipacket.spacket.mlength = 2147483647;
  printf("%s\n", number_to_binary(ipacket.raw));
  ipacket.spacket.message = 255;
  printf("%s\n", number_to_binary(ipacket.raw));
}

我得到(大端):

1111111100000000000000000000000000000000000000000000000000000000
1111111110000000000000000000000000000000000000000000000000000000
1111111111111111100000000000000000000000000000000000000000000000
1111111111111111111111111000000000000000000000000000000000000000
1111111111111111111111111000000011111111111111111111111111111110
1111111111111111111111111000000011111111111111111111111111111110

我的.mlength字段在右侧部分丢失,但它应该在.sid字段旁边。

This page确认它:&#34;包含位字段&#34;的分配单元的对齐方式。但如果是这种情况,那么人们如何将数据打包到比特字段中,这首先是他们的目的呢?

24位似乎是.mlength字段在.message字段被踢出之前可以采取的最大大小。

2 个答案:

答案 0 :(得分:1)

关于位字段布局的几乎所有内容都是在标准中实现定义的,正如您从SO上的许多其他问题中找到的那样。 (除其他外,您可以查看Questions about bitfields,尤其是Bit field's memory management in C)。

如果希望将位字段打包为64位,则必须相信编译器允许您对字段使用64位类型,然后使用:

typedef union {
  uint64_t raw;
  struct {
    uint64_t magic    : 8;
    uint64_t parity   : 1;
    uint64_t stype    : 8;
    uint64_t sid      : 8;
    uint64_t mlength  : 31;
    uint64_t message  : 8;
  } spacket;
} packet_t;

正如最初编写的,在一种似是而非(通用)的方案下,当当前的空间不足够剩余空间时,您的位字段将被拆分为新的32位字。也就是说,magicparitystypesid将占用25位;在32位unsigned int中没有足够的空间来容纳另外31位,因此mlength存储在下一个unsigned int中,并且没有足够的空间留在该单元存储message,以便存储在第三个unsigned int单元中。这将为您提供占用3 * sizeof(unsigned int)或12个字节的结构 - 由于uint64_t上的对齐要求,并集将占用16个字节。

请注意,该标准并不保证我展示的内容会起作用。但是,在许多编译器下,它可能会起作用。 (具体来说,它适用于Mac OS X 10.11.4上的GCC 5.3.0。)

答案 1 :(得分:0)

根据您的体系结构和/或编译器,您的数据将与不同的大小对齐。根据你的观察,我猜你会看到32位对齐的后果。如果你看一下你的联合的大小,并且超过8个字节(64位)的数据已被填充以进行对齐。

对于32位对齐mlength,如果它们总和小于或等于32位,则消息将只能保持彼此相邻。这可能就是你所看到的24位限制。

如果你希望你的结构只采用64位32位对齐,你将不得不重新排列它。单比特奇偶校验应该在31比特mlength的旁边,并且你的4个8比特变量应该组合在一起。