问题
C99 standard告诉我们:
There may be unnamed padding within a structure object, but not at its beginning.
和
There may be unnamed padding at the end of a structure or union.
我假设这也适用于任何 C++ 标准,但我没有检查过它们。
假设在 ARM Cortex-M 上运行的 C/C++ 应用程序(即应用程序中使用两种语言)将在本地介质(例如串行 NOR 闪存芯片)上存储一些持久数据,并读取它电源循环后返回,可能在将来升级应用程序本身之后。升级后的应用程序可能是使用升级后的编译器编译的(我们假设是 gcc)。
让我们进一步假设开发人员是懒惰的(当然不是我),并直接将一些普通的 C 或 C++ struct
流式传输到闪存,而不是首先将它们序列化为任何 偏执狂 经验丰富的开发人员会这样做。
事实上,开发人员是懒惰的,但并非完全无知,因为他已经阅读了AAPCS (Procedure Call Standard for the Arm Architecture)。 .
除了懒惰之外,他的理由如下:
- 他不想打包
struct
以避免应用程序的其余部分出现错位问题。 - AAPCS 为每种基本数据类型指定了固定的对齐方式。
- 填充的唯一合理动机是实现正确对齐。
- 因此,他认为,对于任何 C 或 C++
struct
,填充(以及成员offsetof
和 totalsizeof
)完全由AAPCS。 - 因此,他进一步解释说,我的应用程序不可能无法解释同一应用程序的早期版本会写入的一些回读数据(当然,假设闪存中数据的偏移量内存在写入和读取之间没有变化)。
不过开发者有良心,有点担心:
- C 标准没有提到填充的任何原因。实现正确对齐可能是进行填充的唯一合理原因,但根据标准,编译器可以随意填充任意多的内容。
- 他如何确定他的编译器真的遵循AAPCS ?
- 他的假设会不会突然被他将开始使用的一些明显无关的编译器标志或编译器升级打破?
我的问题是:那个懒惰的开发人员的生活有多危险?换句话说,在上述假设下,C/C++ struct
中的填充有多稳定?
结论
这个问题被问到两周后,唯一的答案是 收到并没有真正回答所提出的问题。我也问过 完全相同的问题 on an ARM community forum , 但根本没有得到任何答复。
但是我选择接受 3246135作为答案,因为:
我认为没有正确答案是非常相关的信息 对于这种情况。软件问题解决方案的正确性 应该是显而易见的。我的问题中所做的假设可能是正确的, 但我不能轻易证明这一点。此外,如果假设是 不正确,在一般情况下,后果可能是 灾难性的。
与风险相比,使用 the answer中暴露的策略似乎 很合理。假设一个恒定的字节顺序(这很容易 强制执行),它是百分百安全的(任何偏差都会产生 编译时的错误)并且它比成熟的要轻得多 序列化。基本上,策略暴露在 the answer是强制性的最低要求 为使一个人的 C/C++
struct
独立于任何 ABI 持久化而付出的代价。
如果您是一名开发人员,问自己上述问题,请回答 不要偷懒,而是使用 accepted 中公开的策略 答案,或保证恒定填充的替代策略 跨软件版本。
最佳答案
您永远无法 100% 确定编译器不会以某种方式引入填充。但是,您可以通过遵循一些规则来降低风险:
- 对所有成员使用固定大小的类型,即
uint32_t
、int64_t
等。 - 每个成员的起始偏移量是成员大小的倍数(或者如果成员是数组/结构,则为最大成员的大小)。
- 避免位域
请注意,这样做可能会引入一些显式填充字段来满足对齐。
例如:
struct orig {
int a;
char b;
int c[10];
short d;
char e[15];
long f;
int g;
};
此结构成员的大小,假设 sizeof(short) == 2
、sizeof(int) == 4
和 sizeof(long) = = 8
,将是 74。如果考虑到可能的填充:
struct orig_padded {
int a;
char b;
char pad1[3];
int c[10];
short d;
char e[15];
char pad2[7];
long f;
int g;
char pad3[4];
};
您的结构大小为 88。
通过一些重新排列,我们可以将尺寸减小回 74:
struct reordered {
int64_t f;
int32_t a;
int32_t c[10];
int32_t g;
int16_t d;
char b;
char e[15];
};
通过按大小降序排列字段,我们基本上删除了字段之间的填充,只在末尾留下潜在的填充。还要注意使用固定尺寸以避免一些意外。然后作为保障,我们添加:
static_assert(sizeof(struct reordered) == 74);
因此,如果结构的编译大小发生变化,您将在编译时知道。
有关更多详细信息,请查看 The Lost Art of Structure Packing .
关于c++ - AAPCS(ARM ABI)下的C/C++结构填充有多稳定?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62397205/