如何在 GCC C 中获取对齐的打包结构数组?
How to get aligned array of packed structs in GCC C?
在 GCC C 中,我怎样才能得到一个压缩结构数组,其中数组中的每个条目都是对齐的?
(FWIW,这是在 PIC32 上,使用 MIPS4000 架构)。
我有这个(简体):
typedef struct __attribute__((packed))
{
uint8_t frameLength;
unsigned frameType :3;
uint8_t payloadBytes;
uint8_t payload[RADIO_RX_PAYLOAD_WORST];
} RADIO_PACKET;
RADIO_PACKETs内部包装。
然后我有RADIO_PACKET_QUEUE,这是一个RADIO_PACKETs的队列:
typedef struct
{
short read; // next buffer to read
short write; // next buffer to write
short count; // number of packets stored in q
short buffers; // number of buffers allocated in q
RADIO_PACKET q[RADIO_RX_PACKET_BUFFERS];
} RADIO_PACKET_QUEUE;
我希望数组 q[] 中的每个 RADIO_PACKET 都从对齐地址(模 4 地址)开始。
但是现在 GCC 没有对齐它们,所以当我试图将 q[n] 读为一个词时,我遇到了地址异常。例如,这给出了一个例外:
RADIO_PACKET_QUEUE rpq;
int foo = *(int*) &(rpq.q[1]);
也许这是因为我声明 RADIO_PACKET 为打包的方式。
我希望每个 RADIO_PACKET 在内部保持打包状态,但希望 GCC 根据需要在每个数组元素后添加填充,以便每个 RADIO_PACKET 从对齐地址开始。
我该怎么做?
您可以将压缩结构包裹在另一个未对齐的结构中。然后你从这个未对齐的结构中做数组。
解决方案 2 可能是,在打包结构的末尾添加虚拟成员 char[]。在这种情况下,您需要以某种方式计算它,可能是手动计算。
我也会建议你重新安排你的结构,首先放置较长的成员,最后放置 uint8_t
个成员(假设你有 16/32 位成员并且不做一些硬件映射)。
根据@Nick 在问题评论中的提示,我想我已经解决了这个问题。
我在 RADIO_PACKET 周围添加了一个 RADIO_PACKET_ALIGNED 包装器。这包括计算的填充。
然后我在RADIO_PACKET_QUEUE结构中用RADIO_PACKET_ALIGNED代替了RADIO_PACKET。
似乎有效:
typedef struct
{
RADIO_PACKET packet;
uint8_t padding[3 - (sizeof(RADIO_PACKET) + 3) % 4];
} RADIO_PACKET_ALIGNED;
typedef struct
{
short read; // next buffer to read
short write; // next buffer to write
short count; // number of packets stored in q
short buffers; // number of buffers allocated in q
RADIO_PACKET_ALIGNED q[RADIO_RX_PACKET_BUFFERS];
} RADIO_PACKET_QUEUE;
感谢所有评论者!
编辑:更便携的包装器版本将使用:
uint8_t padding[(sizeof(int) - 1) - (sizeof(RADIO_PACKET) + (sizeof(int) - 1)) % sizeof(int)];
既然您指定使用 GCC,您应该查看类型属性。特别是,如果您希望 RADIO_PACKET
s 在 4 字节(或更宽)边界上对齐,那么您将在类型上使用 __attribute__((aligned (4)))
。当应用于struct
时,它描述了整体struct
实例的对齐方式,而不是(直接)任何单个成员的对齐方式,因此可以将它与属性[=15=一起使用]:
typedef struct __attribute__((aligned(4), packed))
{
uint8_t frameLength;
unsigned frameType :3;
uint8_t payloadBytes;
uint8_t payload[RADIO_RX_PAYLOAD_WORST];
} RADIO_PACKET;
packed
属性可防止结构元素之间的填充,但它 不会 防止结构表示中的尾部填充,这正是确保所需对齐所必需的指定类型数组的每个元素。然后您不需要在 RADIO_PACKET_QUEUE
.
的声明中做任何特殊的事情
这比您提出的备选方案更简洁明了,但它是特定于 GCC 的。由于您已经是特定于 GCC 的,所以我认为这不是问题。
在 GCC C 中,我怎样才能得到一个压缩结构数组,其中数组中的每个条目都是对齐的?
(FWIW,这是在 PIC32 上,使用 MIPS4000 架构)。
我有这个(简体):
typedef struct __attribute__((packed))
{
uint8_t frameLength;
unsigned frameType :3;
uint8_t payloadBytes;
uint8_t payload[RADIO_RX_PAYLOAD_WORST];
} RADIO_PACKET;
RADIO_PACKETs内部包装。
然后我有RADIO_PACKET_QUEUE,这是一个RADIO_PACKETs的队列:
typedef struct
{
short read; // next buffer to read
short write; // next buffer to write
short count; // number of packets stored in q
short buffers; // number of buffers allocated in q
RADIO_PACKET q[RADIO_RX_PACKET_BUFFERS];
} RADIO_PACKET_QUEUE;
我希望数组 q[] 中的每个 RADIO_PACKET 都从对齐地址(模 4 地址)开始。
但是现在 GCC 没有对齐它们,所以当我试图将 q[n] 读为一个词时,我遇到了地址异常。例如,这给出了一个例外:
RADIO_PACKET_QUEUE rpq;
int foo = *(int*) &(rpq.q[1]);
也许这是因为我声明 RADIO_PACKET 为打包的方式。
我希望每个 RADIO_PACKET 在内部保持打包状态,但希望 GCC 根据需要在每个数组元素后添加填充,以便每个 RADIO_PACKET 从对齐地址开始。
我该怎么做?
您可以将压缩结构包裹在另一个未对齐的结构中。然后你从这个未对齐的结构中做数组。
解决方案 2 可能是,在打包结构的末尾添加虚拟成员 char[]。在这种情况下,您需要以某种方式计算它,可能是手动计算。
我也会建议你重新安排你的结构,首先放置较长的成员,最后放置 uint8_t
个成员(假设你有 16/32 位成员并且不做一些硬件映射)。
根据@Nick 在问题评论中的提示,我想我已经解决了这个问题。
我在 RADIO_PACKET 周围添加了一个 RADIO_PACKET_ALIGNED 包装器。这包括计算的填充。
然后我在RADIO_PACKET_QUEUE结构中用RADIO_PACKET_ALIGNED代替了RADIO_PACKET。
似乎有效:
typedef struct
{
RADIO_PACKET packet;
uint8_t padding[3 - (sizeof(RADIO_PACKET) + 3) % 4];
} RADIO_PACKET_ALIGNED;
typedef struct
{
short read; // next buffer to read
short write; // next buffer to write
short count; // number of packets stored in q
short buffers; // number of buffers allocated in q
RADIO_PACKET_ALIGNED q[RADIO_RX_PACKET_BUFFERS];
} RADIO_PACKET_QUEUE;
感谢所有评论者!
编辑:更便携的包装器版本将使用:
uint8_t padding[(sizeof(int) - 1) - (sizeof(RADIO_PACKET) + (sizeof(int) - 1)) % sizeof(int)];
既然您指定使用 GCC,您应该查看类型属性。特别是,如果您希望 RADIO_PACKET
s 在 4 字节(或更宽)边界上对齐,那么您将在类型上使用 __attribute__((aligned (4)))
。当应用于struct
时,它描述了整体struct
实例的对齐方式,而不是(直接)任何单个成员的对齐方式,因此可以将它与属性[=15=一起使用]:
typedef struct __attribute__((aligned(4), packed))
{
uint8_t frameLength;
unsigned frameType :3;
uint8_t payloadBytes;
uint8_t payload[RADIO_RX_PAYLOAD_WORST];
} RADIO_PACKET;
packed
属性可防止结构元素之间的填充,但它 不会 防止结构表示中的尾部填充,这正是确保所需对齐所必需的指定类型数组的每个元素。然后您不需要在 RADIO_PACKET_QUEUE
.
这比您提出的备选方案更简洁明了,但它是特定于 GCC 的。由于您已经是特定于 GCC 的,所以我认为这不是问题。