首页 > 解决方案 > 用于未来增长的结构末尾的填充物

问题描述

Ulrich Drepper 的这份名为“图书馆设计、实施和维护的良好实践”的文档说(第 5 页底部)

[...] 类型定义应始终创建至少最少数量的填充以允许未来增长

[...]

其次,一个结构应该在末尾包含一定数量的填充字节。

struct the_struct
{
  int foo;
  // ...and more fields
  uintptr_t filler[8];
};

[...]

如果以后必须将字段添加到结构中,则可以将类型定义更改为:

struct the_struct
{
  int foo;
  // ...and more fields
  union
  {
    some_type_t new_field;
    uintptr_t filler[8];
  } u;
};

我看不出在结构末尾添加这个填充物的意义。是的,这意味着当向new_field结构添加新字段 ( ) 时,它实际上并没有增长。但是,将新字段添加到您不知道您将需要它们的结构中的全部意义不在于它们吗?在此示例中,如果您想添加的不是一个字段而是 20 个字段怎么办?然后您是否应该使用 1k 字节的填充符以防万一?另外,为什么结构的大小在库的后续版本中不改变很重要?如果库提供了干净的抽象,那应该没关系吧?最后,使用 64 字节填充符(8 uintpr_t(是的,它不一定是 64 字节))听起来像是在浪费内存......

该文件根本没有详细说明这一点。你有什么解释为什么这个建议“在结构的末尾添加填充物以计划未来的增长”是一个好的建议吗?

标签: cstruct

解决方案


根据情况,是的,结构的大小对于二进制兼容性可能很重要。

考虑stat()。它通常这样称呼:

struct stat stbuf;
int r = stat(filename, &stbuf);

使用这种设置,如果stat结构的大小发生变化,每个调用者都会变得无效,并且需要重新编译。如果被调用代码和调用代码都是同一个项目的一部分,那可能不是问题。但是如果(就像stat()Unix/Linux 内核的系统调用一样)有很多很多的调用者,实际上不可能强制它们全部重新编译,所以这意味着stat结构永远无法改变。

这类问题主要出现在调用者分配(或检查/操作)结构的实际实例时。另一方面,如果结构的内部只被库代码分配和操作——如果调用代码只处理指向结构的指针,而不试图解释指向的结构——它可能结构是否改变无关紧要。

(现在,综上所述,如果结构必须更改大小,可以采取各种其他措施来缓解问题。在某些库中,调用者分配结构的实例,然后将两个指针都传递给结构,以及调用者知道的结构的大小,一直到库代码中。然后,较新的库代码可以检测到不匹配,并避免设置或使用较旧的调用者未为其分配空间的较新字段。我相信gcc 至少实现了特殊的钩子,以便 glibc 可以实现同一结构的多个版本,以及使用它们的库函数的多个版本,以便可以使用与特定结构的版本相对应的正确库函数来电者正在使用。返回stat(),例如,在 Linux 下,至少有两种不同版本的stat结构,一种为文件大小分配 32 位,另一种分配 64 位。)


推荐阅读