c - __attribute__((packed)) 对嵌套结构数组的影响?

标签 c arrays gcc attributes struct

问题

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

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 字段上设置打包属性,当这不起作用时,通过在上面的代码中设置 arrayZ 上的打包属性进行测试:

packed_1_s structArray[10] __attribute__((packed));

uint8_t arrayZ[20] __attribute__((packed));

两次尝试都给了我一个编译器警告,提示在此上下文中无法理解打包属性并将被跳过(我使用“-Wall”构建的好东西)。

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

注意事项

从我可以从 GCC 文档中确定的情况来看,似乎有 3 种主要情况下插入了额外的内存。

  1. Structure contents may have additional memory allocated to the structure itself to change the spacing between fields.
    Effectively, the definition of the memory map of the structure contents within the structure may change (though not the order of the elements).
  2. Structures may have additional memory allocated to them to fill them out to a more efficient overall size. This is generally intended so other variables coming after a declaration of one of their instances doesn't fall within the same "block" as the structure instance where a "block" is defined by the system/compiler.
  3. Arrays, whether within a structure or not, may have additional memory added to them to shift the elements to an efficient alignment.


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

问题

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

编辑

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

  1. Larger offsets in between fields in the memory map of the structure.
    The space between the fields can be modified to align each field. This can be changed using the packed or align() attributes.
  2. Structure end padding. The size for a structure, as returned by sizeof(), can be modified so arrays of the structures end up correctly aligned for the system. This allows all systems to assume that the start of structures will always be aligned, causing issues if they aren't. This seems unaffected by the pack or align attributes.


由于新的情况 2,结构中的数组元素不一定遵守结构上指定的打包或 align() 属性,尽管数组的开头和紧随数组的字段这样做。

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

最佳答案

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

  • packed在结构声明中使用时,它将压缩其字段,例如 sizeof(structure) == sizeof(first_member) + ... + sizeof(last_member)。
  • 这里,一个数组就是 一名成员的结构。打包数组的包含结构不会改变数组的大小。实际上,(any) 数组的大小是 总是 sizeof(element) * number_of_elements。
  • 类似地,包装内部结构的包含结构不会改变内部结构的大小。结构的大小是完全由其声明 决定, 无论你在哪里使用都是一样的。
  • 打包一个结构将使其所需的对齐一个字节(即它可以放在内存中的任何位置)。
  • 访问打包结构的字段时,打包将引入对齐问题。当直接访问字段时,编译器会考虑到这一点,但是 不是通过指针访问它们时 .当然,这不适用于具有所需对齐方式的字段(例如 char 或其他打包结构)。见 my answer to a similar question ,其中包括一个演示通过指针访问成员的问题的程序。

  • 最后,回答这个问题,

    Is there any way to guarantee that the data_s structure will have absolutely no additional space added to it or any of its sub-structures so I don't have compiler dependent shifts in the memory map?



    是的。递归地声明该结构以及它包含的所有结构。

    另请注意,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
    

    您对数组提出的另外两点是正确的-但仅在未打包结构时才如此。打包强制结构的字段是连续的,这确实会产生对齐问题,如果不使用打包,可以通过在成员之间插入空白空间并填充结构来解决(请参阅我已经提出的关于对齐的观点)。

    关于c - __attribute__((packed)) 对嵌套结构数组的影响?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7957363/

    相关文章:

    gcc - 为什么 C 允许缺少函数声明?

    c - GCC 将两个不同的目标文件合并为一个目标文件

    c - 逐字读取数据

    c - 如何区分16位MZ和32位MZ

    检查C中的整个字符数组是否只是数字,如果是则返回true?

    javascript - 使用给定数字对 JSON 数组进行分组

    javascript - Javascript 对象属性和数组之间的迭代有什么区别

    java - 如何在android中的文件中包含字符串数组中的路径

    c - 是否可以使用 SonarQube 分析 Linux C 项目?

    c - 结构到结构赋值后的奇怪错误