位域和对齐
Bitfields and alignment
正在尝试将数据打包到数据包中。这个数据包应该是 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] = '[=11=]';
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 确认:持有位字段的分配单元的对齐是未指定。 但是如果这是在这种情况下,人们如何将数据打包到位字段中,这首先是他们的目的?
24 位似乎是 .mlength
字段在踢出 .message
字段之前能够采用的最大大小。
根据您的架构 and/or 编译器,您的数据将对齐到不同的大小。根据您的观察,我猜您看到的是 32 位对齐的结果。如果您查看 union 的 sizeof 并且超过 8 个字节(64 位)数据已被填充以进行对齐。
使用 32 位对齐,mlength 和消息只有在加起来小于或等于 32 位时才能彼此相邻。这可能是您看到的 24 位限制。
如果您希望您的结构仅采用 64 位和 32 位对齐,您将不得不稍微重新排列它。一位奇偶校验应该在 31 位 mlength 旁边,你的 4 个 8 位变量应该组合在一起。
几乎所有关于位域布局的内容都是在标准中由实现定义的,正如您从关于 SO 的主题的许多其他问题中发现的那样。 (除其他外,您可以查看 Questions about bitfields and especially )。
如果你希望你的位域被打包成 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;
正如最初所写,在一种合理的(常见)方案下,当当前位域中没有足够的 space 剩余时,您的位域将被拆分为新的 32 位字。即magic
、parity
、stype
、sid
占用25位; 32 位 unsigned int
中没有足够的空间来容纳另外 31 位,因此 mlength
存储在下一个 unsigned int
中,并且没有足够的 space ] 留在该单元中以存储 message
,以便存储在第三个 unsigned int
单元中。这将为您提供一个占用 3 * sizeof(unsigned int)
或 12 个字节的结构 — 由于 uint64_t
.
上的对齐要求,联合将占用 16 个字节
请注意,该标准不保证我展示的内容一定有效。但是,在许多编译器下,它可能会起作用。 (具体来说,它适用于 Mac OS X 10.11.4 上的 GCC 5.3.0。)
正在尝试将数据打包到数据包中。这个数据包应该是 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] = '[=11=]';
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 确认:持有位字段的分配单元的对齐是未指定。 但是如果这是在这种情况下,人们如何将数据打包到位字段中,这首先是他们的目的?
24 位似乎是 .mlength
字段在踢出 .message
字段之前能够采用的最大大小。
根据您的架构 and/or 编译器,您的数据将对齐到不同的大小。根据您的观察,我猜您看到的是 32 位对齐的结果。如果您查看 union 的 sizeof 并且超过 8 个字节(64 位)数据已被填充以进行对齐。
使用 32 位对齐,mlength 和消息只有在加起来小于或等于 32 位时才能彼此相邻。这可能是您看到的 24 位限制。
如果您希望您的结构仅采用 64 位和 32 位对齐,您将不得不稍微重新排列它。一位奇偶校验应该在 31 位 mlength 旁边,你的 4 个 8 位变量应该组合在一起。
几乎所有关于位域布局的内容都是在标准中由实现定义的,正如您从关于 SO 的主题的许多其他问题中发现的那样。 (除其他外,您可以查看 Questions about bitfields and especially
如果你希望你的位域被打包成 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;
正如最初所写,在一种合理的(常见)方案下,当当前位域中没有足够的 space 剩余时,您的位域将被拆分为新的 32 位字。即magic
、parity
、stype
、sid
占用25位; 32 位 unsigned int
中没有足够的空间来容纳另外 31 位,因此 mlength
存储在下一个 unsigned int
中,并且没有足够的 space ] 留在该单元中以存储 message
,以便存储在第三个 unsigned int
单元中。这将为您提供一个占用 3 * sizeof(unsigned int)
或 12 个字节的结构 — 由于 uint64_t
.
请注意,该标准不保证我展示的内容一定有效。但是,在许多编译器下,它可能会起作用。 (具体来说,它适用于 Mac OS X 10.11.4 上的 GCC 5.3.0。)