__attribute __((packed))对嵌套结构数组的影响?

问题

我正在努力通过网络将原始结构发送到另一端的已知程序,但不得不担心用于对齐结构的静默引入的内存(其他问题,如字节顺序被覆盖)。 我正在使用的是:

typedef struct __attribute__((packed)) { uint16_t field1; uint16_t field2; uint16_t field3; } packed_1_s; typedef struct __attribute__((packed)) { uint16_t fieldA; uint16_t fieldB; packed_1_s structArray[10]; } packed_2_s; typedef struct __attribute__((packed)) { uint16_t fieldX; packed_2_s fieldY; uint8_t arrayZ[20]; } data_s; 

我理解通常,packed_1_s结构可以/将为结构的每个实例分配额外的空间,以将其填充到编译器的优选大小(取决于它所构建的硬件),并且有利的大小可以是2个字节的任何位置到64个字节(最近)。 通常情况下,如果我在packed_2_s中有一个packed_1_s的实例就没有问题,但是当我尝试将元素放入数组时,我会理解它们存在一些差异。

试图解决方案

gcc文档似乎建议通过简单地在packed_2_s定义中包含packed属性,即使它们是数组,这些字段也将尽可能紧密地包装,并且不会为packed_2_s结构添加空间以对齐元素数组。 关于align()属性的文档虽然表明数组的处理方式与其他字段不同,但需要在字段上直接设置align / packed属性,以便修改添加的额外间距以匹配指定的对齐(或缺少对齐)。 我尝试在structArray字段上设置packed属性,当它不起作用时,通过在上面的代码中设置arrayZ上的packed属性进行测试:

 packed_1_s structArray[10] __attribute__((packed)); uint8_t arrayZ[20] __attribute__((packed)); 

两次尝试都给了我一个编译器警告,在这个上下文中没有理解packed属性并且会被跳过(我用“-Wall”构建的好东西)。

我希望绕过这个问题的方法是使用属性align(1),指示1字节的所需对齐,这与packed属性相当,但是文档说align()属性只能增加对齐和打包应该用于减少对齐。

注意事项

根据我从GCC文档中可以确定的内容,似乎有3个主要的附加内存插入案例。

  1. 结构内容可以具有分配给结构本身的附加存储器以改变字段之间的间隔。
    实际上,结构内结构内容的存储器映射的定义可以改变(尽管不是元素的顺序)。
  2. 结构可以具有分配给它们的额外存储器以将它们填充到更有效的总体大小。 这通常是为了使声明其实例之一之后的其他变量不属于与系统/编译器定义“块”的结构实例相同的“块”。
  3. 无论是否在结构内,数组都可以添加额外的内存以将元素转换为有效对齐。

据我所知,packed属性可用于影响结构并阻止上面案例1和2中添加的额外内存,但似乎没有办法在我的编译器上处理上面的案例3(s )。

问题

有没有办法保证data_s结构绝对没有额外的空间添加到它或它的任何子结构,所以我没有内存映射中的编译器相关的移位? 我是否误解了编译器可以插入空间以故意转移内存映射的情况?

编辑

我与当地大师讨论了一些问题,听起来我对上面的案例3有一些误解。 数组中的元素之间没有插入空格,但保证它们正确对齐的额外空间会添加到结构本身。 显然,这表明诸如“sizeof(structureOnlyContaining_uint32_t)”之类的内容并不总是返回“4”,因为可能会添加额外的空格以对齐正在使用的编译器上的uint32_t数据类型。 结果是真的只有2个案例:

  1. 结构的存储器映射中的字段之间的较大偏移。
    可以修改字段之间的空间以对齐每个字段。 这可以使用packed或align()属性进行更改。
  2. 结构末端填充。 可以修改sizeof()返回的结构大小,以便最终为系统正确对齐结构数组。 这允许所有系统假设结构的起始始终是对齐的,如果不是则导致问题。 这似乎不受pack或align属性的影响。

由于新的情况2,结构中数组的元素不一定遵循结构上指定的packed或align()属性,尽管数组的开头和紧跟在数组之后的字段也是如此。

我的问题是关于如何处理packed_2_s中的structArray,因为整个数组的大小不能完全由packed属性保证。 有没有办法保证structArray字段的固定大小整体? 应该注意的是,我不能过多地增加packed_1_s的大小,因为data_s struct需要保持尽可能小(它在流式方案中替换音频/video数据)。

请注意关于__attribute__((packed))的以下几点:

  • 当在结构声明中使用packed ,它将压缩其字段,例如sizeof(structure)== sizeof(first_member)+ … + sizeof(last_member)。

  • 这里,数组只是结构的一个成员 。 打包数组的包含结构不会改变数组的大小。 实际上,(任意)数组的大小总是 sizeof(element)* number_of_elements。

  • 类似地,包装内部结构的包含结构将不会改变内部结构的尺寸。 结构的大小完全取决于其声明 ,无论您在何处使用,都是相同的。

  • 打包结构将使其所需的对齐为一个字节(即它可以放在内存中的任何位置)。

  • 在访问打包结构的字段时,打包将引入对齐问题。 编译器将在直接访问字段时对此进行说明,但在通过指针访问字段时则不会 。 当然,这不适用于具有所需对齐的字段(例如char或其他打包结构)。 请参阅我对类似问题的回答 ,其中包括一个程序,用于演示通过指针访问成员的问题。

最后,回答这个问题,

有没有办法保证data_s结构绝对没有额外的空间添加到它或它的任何子结构,所以我没有内存映射中的编译器相关的移位?

是。 声明结构为packed,以及它包含的所有结构,递归。

另请注意,packed属性适用于结构声明,而不适用于类型。 没有声明为非打包的结构的打包版本 。 当你在某个地方使用一个结构时,它(它的成员)将被打包,当且仅当结构本身被声明为打包时。 结构的大小完全由其声明决定,这有点暗示。

更新 :由于某种原因,你仍然对数组感到困惑。 我提供的解决方案(声明所有结构已打包)也适用于数组 。 例如:

 struct elem_struct { uint32_t x; } __attribute__((packed)); // packed guarantees that sizeof(struct elem_struct) = sizeof(uint32_t) = 4 struct array_struct { struct elem_struct arr[10]; } __attribute__((packed)); // packed guarantees that sizeof(struct array_struct) = // = sizeof(struct elem_struct[10]) = 10 * sizeof(struct elem_struct) // = 10 * 4 = 40 

关于数组的两个附加点是正确的 – 但仅在结构未打包时才有效。 打包强制结构的字段是连续的,这确实会产生对齐问题,如果没有使用打包,将通过在成员之间插入空格并填充结构来解决(请参阅我已提出的关于对齐的观点)。