【问题标题】:How to properly resolve increase in pointer alignment with clang?如何正确解决指针对齐增加的问题?
【发布时间】:2015-10-19 15:55:54
【问题描述】:

考虑以下结构:

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 标志。

【问题讨论】:

  • some_struct *pstruct_second_t *s 不可互换(指向结构的指针始终指向其第一个成员,但事实并非如此)。你的意思是first_struct_t *s = (first_struct_t *) p->f;
  • p->f 不是 first_struct_t 吗?不是first_struct_t *
  • 你是对的。我修正了无效的措辞。
  • stackoverflow.com/questions/10951039/… 尝试强制first_struct_t 8 字节对齐。

标签: c struct casting clang memory-alignment


【解决方案1】:

我假设当你有 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 的异常。

您要完成的第二件事类似于 Linux 内核中的 container_of 宏。 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*

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

补充:

似乎 mozilla 也通过转换为 void* 来解决此问题:

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

【讨论】:

  • 我正在使用->f 变量来存储指向second_struct_t 实例的指针。基本上,这是一种廉价的多态性方法。您可以使用->f 访问first_struct_t,如果您知道,您可以访问second_struct_t。在这种情况下,我知道 second_struct_t 并愿意这样投射。
猜你喜欢
  • 2018-01-17
  • 2021-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多