【问题标题】:Warning for struct padding in ClangClang中结构填充的警告
【发布时间】:2016-11-18 06:20:31
【问题描述】:

我创建了以下结构:

typedef struct  s_fct_printf
{
  char          flag;
  void          (*fct)(void*);
}               t_fct_printf;

static const t_fct_printf       flags[] =
{
  { 's', my_putstr_printf },
  //[...]
  { 'b', display_base_2 },
};

但是当我用 clang -Weverything 编译时,我有以下警告:

warning: padding struct 'struct s_fct_printf' with 7 bytes to
      align 'fct' [-Wpadded]

我找到了以下解决方案:

typedef struct  s_fct_printf
{
  char          flag;
  void          (*fct)(void*);
  char          pad[7];
}               t_fct_printf;

但这并没有解决问题:

warning: missing field 'pad' initializer [-Wmissing-field-initializers]
    { 'b', display_base_2 },
warning: padding struct 'struct s_fct_printf' with 7 bytes to
      align 'fct' [-Wpadded]

所以我尝试了:

typedef struct  s_fct_printf
{
  char          flag;
  char          pad[7];
  void          (*fct)(void*);
}               t_fct_printf;

但出现以下错误:

warning: incompatible pointer to integer conversion initializing 'char'
      with an expression of type 'void (void *)' [-Wint-conversion]
    { 'b', display_base_2 },
warning: suggest braces around initialization of subobject
      [-Wmissing-braces]
    { 'b', display_base_2 },
warning: missing field 'fct' initializer [-Wmissing-field-initializers]
    { 'b', display_base_2 },
error: initializer element is not a compile-time constant
    { 's', my_putstr_printf },

我找到的最后一个解决方案是,但我读到它没有优化,因为编译器不再打包我的变量。

typedef struct __atribute__((__packed__))       s_fct_printf
{
  char          flag;
  void          (*fct)(void*);
}                                               t_fct_printf;

有好的解决办法吗?

【问题讨论】:

  • 你还没有说明问题...
  • 好吧,我的问题是,当我用 clang 编译时,我有这个警告:padding struct 'struct s_fct_printf' with 7 bytes to align 'fct' [-Wpadded]
  • 可以安全地忽略和抑制此警告。填充结构是编译器经常做的完全有效的事情。除非你依赖于特定的布局,当然,这不是一件好事..
  • 您应该禁用填充警告;它对正常的程序代码没有用。阅读大量警告选项并添加所需的选项可能是一种更好的方法。这样你也知道它们的意思(如果你读过文档,你会发现的,顺便说一句)。
  • 这篇文章不错:catb.org/esr/structure-packing

标签: c padding


【解决方案1】:
typedef struct  s_fct_printf
{
  char          flag;
  char          pad[7];
  void          (*fct)(void*);
}               t_fct_printf;

有利于处理填充问题。但是,您必须更改初始化数组的方式。

使用

static const t_fct_printf       flags[] =
{
  { 's', "", my_putstr_printf },
  { 'b', "", display_base_2 },
};

否则,编译器会尝试使用my_putstr_printf 初始化成员pad,这不是您想要的。

更新

您可以避免使用硬编码数字7 来表示pad 的大小,方法是:

typedef struct  s_fct_printf
{
  char          flag;
  char          pad[sizeof(void(*)(void))-1];
  void          (*fct)(void*);
}               t_fct_printf;

感谢@WeatherVane 的建议。

【讨论】:

  • 我正要评论这个问题,OP 的第一个解决方案中建议的填充位于函数指针成员的错误一侧。但是7 可以在编译时用sizeof 计算吗?
  • @WeatherVane,优秀的建议。
  • 但是为什么还要填充呢?编译器已经这样做了。编译器只是通知你它填充了。
  • @PaulOgilvie,好问题。我猜这与团队采用的编码实践有关。
  • 为什么要关心填充?该警告仅在您担心它时才有用,并且通常仅适用于少数structs(在这些周围使用#pragma)。即使在那里,您最好使用包装(可能带有警告以确保完全安全)。此外,sizeof 方法不一定会生成正确的填充。例如struct { uint8_t a; /*manual padding with sizeof*/ uint64_t b;} 将始终添加 7 个字节,尽管在例如32 位 CPU 3 就足够了。最好使用_Alignof(next_element) - (sizeof(previous_element)(不过,我会禁用警告)
【解决方案2】:

我充分考虑了您的问题。我不认为添加填充字段是解决方案。它破坏了代码并引入了潜在的未来问题。

我还了解所有代码都应在没有警告或错误的情况下编译的质量要求。但是,此警告仅提供信息,并不指出可能的错误。

我的建议是在它发生和接受发生的那些点上明确禁止此警告。我建议(以VC为例):

#pragma warning(disable:4123)
// The compiler will inform that padding will insert 7 bytes after flag,
// which will be unused. This is acceptable.
typedef struct  s_fct_printf
{
  char          flag;
  void          (*fct)(void*);
}               t_fct_printf;
#pragma warning(enable:4123)

我希望你的编译器有类似的机制。

【讨论】:

    【解决方案3】:

    您似乎在 64 位系统上运行。 char 占用一个字节,编译器希望函数指针从 64 字边界开始。因此,它需要在char 之后填充 7 个字节来对齐函数指针。

    编译器似乎只是通知你,但你没有出错。

    【讨论】:

      猜你喜欢
      • 2021-09-17
      • 1970-01-01
      • 1970-01-01
      • 2011-08-13
      • 1970-01-01
      • 2023-04-03
      • 2014-10-03
      相关资源
      最近更新 更多