【问题标题】:Will packing a struct ever misalign adjacent memory?打包一个结构会不会错位相邻的内存?
【发布时间】:2019-07-20 03:03:41
【问题描述】:

考虑以下结构

struct a{
    uint16_t foo1
    uint16_t foo2
    uint32_t foo3
    uint64_t foo4
    uint16_t foo5
    uint16_t foo6
}__attribute__(packed);

它有 20 个字节长。没关系,因为结构中的所有内容都与单词边界对齐。

但是,如果善意的开发人员执行以下操作会发生什么:

static struct a foo;
static uint64_t b;

理论上,这会使 b 跨单词边界错位。

有趣的是,gcc 似乎将 foo 对齐到 16 个字节,但是(如果我对汇编的了解是正确的),它允许 b 未对齐:

.local  foo.2476
.comm   foo.2476,20,16
.local  b.2477
.comm   b.2477,8,8

我在这里遗漏了什么,或者这是结构打包危险的一个例子?

【问题讨论】:

  • 结构体的起始地址总是对齐的,与打包无关。或者至少大多数编译器都是这样的。
  • 我知道在数组中,struct a foo[2] 不会对齐。那么struct a foo1; struct a foo2; 呢,您是否建议编译器无论如何都会添加填充?
  • 数组是另一回事。普通的结构数组将在每个对象的末尾添加填充。 C 标准规定数组对象必须相邻分配。因此,如果您删除尾随填充,编译器别无选择,只能分配未对齐的下一个结构项。
  • 谁说b 从 4 字节边界开始是错位的?对齐要求是特定于体系结构的,多字节类型的对齐要求可以小于该类型的大小。
  • 除非你把它放到另一个打包结构中,否则什么应该阻止编译器在变量之间添加所需的填充?

标签: c struct padding


【解决方案1】:

但是,如果善意的开发人员执行以下操作会发生什么:

static struct a foo;
static uint64_t b;

理论上,这会使 b 跨单词边界错位。

不,在这种情况下,b 没有特别的原因会错位,就像你有错位一样

static char foo;
static uint64_t b;

。假设 uint64_t 的对齐要求大于 1,那么在这种情况下,质量差的编译器可能b 不对齐,但实际上,您不太可能看到这样的结果。编译器自行决定为变量分配存储空间,并且通常位于对齐良好的地址。

有趣的是,gcc 似乎将 foo 对齐到 16 个字节,但是(如果我对汇编的了解是正确的),它允许 b 未对齐: [...]

我在这里遗漏了什么,或者这是结构打包危险的一个例子?

您几乎肯定会遗漏一些东西。我向甜甜圈打赌,您机器的 uint64_t 有 4 字节而不是 8 字节的对齐要求。

【讨论】:

  • 更确切地说,考虑到标准,编译器会错位对齐这些东西。
  • 不一定,@Swordfish。在可以成功执行(硬件)未对齐访问的架构上,实现可以在硬件意义上随机甚至系统地未对齐变量,但仍完全符合标准。
  • 我的意思是考虑到标准的对齐要求而未对齐。
  • 我明白你的意思,@Swordfish,但标准允许实现为自己选择类型的 C 级对齐要求。因此,实现无法满足标准对对象对齐的要求几乎没有意义。事实上,我认为除了与_Alignof 的一致性和对_Alignas 的遵守之外,在语言级别(忽略标准库)没有none
猜你喜欢
  • 2018-08-13
  • 1970-01-01
  • 2022-01-15
  • 1970-01-01
  • 2019-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-27
相关资源
最近更新 更多