如何正确解决指针对齐增加的问题?

How to properly resolve increase in pointer alignment with clang?

考虑以下结构:

typedef struct {
  uint32_t foo;
  uint32_t bar;
} first_struct_t;

typedef struct {
  first_struct_t f;
  uint8_t *p;
  uint8_t buf[];
} second_struct_t;

但是,稍后在我的代码中,发生了以下分配:

typedef struct {
  first_struct_t *f;
  // ...
} some_struct;

int function_before_foo(some_struct *p) {
  p->f = (second_struct_t *) malloc(sizeof(second_struct_t));
  // ...
}

int function_foo(some_struct *p) {
  second_struct_t *s = (second_struct_t *) p->f;
  // ...
}

并且由于-Wcast-align而产生以下错误:

'second_struct_t *' increases required alignment from 4 to 8 [-Werror,-Wcast-align]
second_struct_t *s = (second_struct_t *) p->f;

一个解决方案是将其转换为 void *,但这似乎只能掩盖问题。最干净的解决方案是什么?

编辑: 这只发生在 clang 而不是 GCC 上,不管这两个编译器都被赋予了 -Wcast-align 标志。

我假设当您有 struct_second_t 时,您实际上是指 second_struct_t,否则您的代码无法编译。

问题是 some_struct 中的 f 是指向 first_struct_t 的指针,而不是指向 second_struct_t 的指针。

我应该补充一点,struct_second_t *s = (struct_second_t *) p->f; 中的演员隐藏了警告消息。一般来说,如果你必须将一个指针转换为另一个指针,你往往不会导致未定义的行为。这并不总是正确的,但却是一个很好的指导方针。

回应评论。

首先,对于 x86(32 位和 64 位)的 gcc,您似乎不会收到该警告,因为通用寄存器没有对齐要求,尽管对齐可以提高性能(请参阅此 SO post 了解更多信息).至于为什么 clang 会发出该警告,可能是因为性能或者它们没有像 gcc 对 x86 那样的异常。

其次,您要完成的工作类似于 container_of 宏在 Linux 内核中所做的工作。 container_of 通常定义为:

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

在搜索中,我发现唯一可以解决您的问题的是 this commit to tup,它更改了 container_of 的版本以包含对 void*.[=34= 的强制转换]

基本上,我认为问题出在您知道 p->f 实际上指向一个 second_struct_t,而您的编译器并没有因此发出警告。所以你要么不这样做,要么转换为 void*.

补充:

mozilla 似乎也通过转换为 void* 解决了这个问题:

最后,我建议查看 container_of 并使用它,而不是依赖结构的第一个元素是另一个结构这一事实。这样你的代码更有弹性,如果其他人改变了结构成员的顺序,它仍然可以工作。您必须将 void* 添加到 container_of 才能避免警告。请注意,在存在对齐问题的体系结构上,假设您始终正确地在子项和父项之间进行类型更改,则在运行时应该不会出现问题。