【问题标题】:Effects of __attribute__((packed)) on nested array of structures?__attribute__((packed)) 对嵌套结构数组的影响?
【发布时间】:2011-12-18 22:22:39
【问题描述】:

问题

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

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() 属性只能 增加对齐和packed应该用于减少对齐。

注意事项

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

  1. 结构内容可能有额外的内存分配给 结构本身以更改字段之间的间距。
    有效地定义结构的内存映射 结构中的内容可能会改变(虽然不是顺序 元素)。
  2. 结构可能有额外的内存分配给它们来填充 它们的整体尺寸更有效。这一般是 打算让其他变量在声明其中之一之后出现 它们的实例与 结构实例,其中“块”由系统/编译器定义。
  3. 数组,无论是否在结构内,都可能有额外的 添加到它们的内存以将元素转换为有效的对齐方式。

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

问题

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

编辑

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

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

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

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

【问题讨论】:

  • 你试过#pragma pack(1)
  • 如果我没记错的话,我相信 #pragma pack() 大致相当于 pack 属性和 align() 属性组合成一个 pragma 调用。此外,如果您查看属性类型存在的原因,它们被有意定义为非编译指示选项,我想利用的原因(特别是如果不支持,它们会在编译时生成错误/警告)编译器)
  • 诸如“sizeof(uint32_t)”之类的东西总是会返回他们总是为该平台返回的任何东西。 uint32_t 不是结构/联合,它是标量类型。
  • 好吧,不好的例子。 sizeof(structureOnlyContaining_uint32_t) 可能是一个更好的例子。编辑了原件以反映更正。
  • 不要使用压缩结构。编写适当的序列化函数。

标签: c arrays gcc attributes struct


【解决方案1】:

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

  • packed 用于结构声明时,它将压缩其字段,例如 sizeof(structure) == sizeof(first_member) + ... + sizeof(last_member)。

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

  • 同样,包装内部结构的包含结构不会改变内部结构的大小。结构的大小完全由它的声明决定,无论你在哪里使用都是一样的。

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

  • 在访问打包结构的字段时,打包会引入对齐问题。编译器会在直接访问字段时考虑这一点,但不会在通过指针访问时考虑。当然,这不适用于具有所需对齐方式的字段(例如 char 或其他打包结构)。请参阅my answer to a similar question,其中包含一个演示通过指针访问成员的问题的程序。

最后,回答这个问题,

有没有办法保证data_s结构会有 绝对没有额外的空间添加到它或它的任何 子结构,所以我在内存中没有编译器依赖的变化 地图?

是的。以递归方式将结构及其包含的所有结构声明为已打包。

还要注意,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

关于数组的另外两点是正确的——但只有在结构没有被打包的情况下。打包强制结构的字段是连续的,这确实会产生对齐问题,如果不使用打包,则可以通过在成员之间插入空白空间并填充结构来解决(参见第 I 点已经提出了对齐)。

【讨论】:

  • 您对打包属性的描述正是我最初希望它会做的,但是 GCC 文档以一种似乎暗示打包结构将具有以下属性的方式描述它: sizeof(structure ) == sizeof(member1)+...+sizeof(memberN) + (无论添加了什么额外的空间来保持结构对齐的变量)
  • 另外,仅供参考,可以在“结构”一词之后立即指定属性,或者在您显示的结构定义之后完成。 link: GCC description of Type Attributes
  • 只花了一分钟的时间来运行测试,你完全正确地删除了 packed 属性(无论添加了什么额外的空间来保持结构对齐的变量)。显然,这只是 GCC 对打包属性的描述所暗示的,因为没有强制执行结构对齐,这是唯一一次在尾部增加结构大小。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-31
  • 1970-01-01
  • 2021-09-21
  • 1970-01-01
  • 2015-08-14
  • 2022-01-09
相关资源
最近更新 更多