【问题标题】:Is this a portable way to make a stack buffer eight-byte aligned?这是使堆栈缓冲区八字节对齐的可移植方式吗?
【发布时间】:2014-11-22 06:56:57
【问题描述】:
struct {
  uint64_t a; char z[120];
} b;

...

struct buffer_with_alignment_requirement* c = (struct buffer_w*)&b;
c->start_using_it;

如果没有第一个成员a,我们可能会在访问缓冲区中的字段时崩溃。简单地添加这样的成员来强制对齐整个结构足够严格是否可移植且正确?

这不是关于指针别名的问题,只是关于堆栈上b的地址是否总是八字节对齐。

【问题讨论】:

  • 否 - 这取决于平台、编译器和编译器开关。
  • 什么操作系统? posix_memalign 和 memalign 将在 Linux/UNIX 中为您强制对齐。
  • 太不可思议了!如果可以的话,我会否决我自己的帖子。十分钟内 5 次投票赞成基本上是“让我在标准中为您搜索”的问题。我的最后一个问题(stackoverflow.com/questions/15294518)实际上很有趣并且被忽略了:(。
  • 为什么不直接使用编译器编译指示来对齐?
  • @Mellowcandle 编译器编译指示通常是不可移植的,并且很少有编译器具有用于对齐的编译指示。 (例如,更常用的#pragma pack 并不能解决 OP 的问题)。

标签: c


【解决方案1】:

精确宽度整数类型,如果它们存在 (7.20.1.1p3),没有任何特定的对齐要求,除了它们应该至少与 char 对齐并且不比 max_align_t 对齐( 6.2.8p2-4)。甚至没有任何要求对齐应该遵循整数转换等级,当然相应的有符号和无符号类型应该具有相同的对齐(6.2.5p6)。

如果您想要特定的对齐方式,请使用对齐说明符:

#include <stdalign.h>
struct alignas(8) {
  char z[120];
} b;

但是,大概考虑到您对访问字段的描述,您实际上想要的是将缓冲区与缓冲区中可能存在的最对齐类型对齐。为此,C11 提供了max_align_t;在较旧的编译器中,您可以使用标准类型的联合来模拟 max_align_t

typedef union {
  char c;
  short s;
  int i;
  long l;
  long long ll;  // if available
  float f;
  double d;
  long double ld;  // if available
  void *p;
  void (*fun)();
} max_align_t;

【讨论】:

  • 你的意思是写_Alignas
  • 好的,也可以。不过,我更喜欢在这里使用关键字。 (奇怪,因为我更喜欢#include &lt;stdbool.h&gt; 并使用bool 而不是_Bool。)
  • 好的,那么像这样的代码呢:fxr.watson.org/fxr/source/sysdeps/unix/sysv/linux/bits/… (sockaddr_storage) 我在所谓的可移植应用程序的代码中也看到过这种事情。如果您使用文字双精度而不是 uint64_t,那么您会对这个技巧感到满意吗?我的所有平台上都没有 stdalign.h。
  • @NicholasWilson 也没有要求double 对齐到 8 个字节,因此这是否有效取决于平台(通常,“便携”只是意味着可移植到“标准”平台)。跨度>
  • @NicholasWilson 你可以使用(或模仿)max_align_t;见上文。
【解决方案2】:

不,C 标准让编译器自行决定对齐。

在实践中,许多编译器会将 struct 对齐为 8 字节对齐,但并非必须这样做。

【讨论】:

    【解决方案3】:

    这取决于您所说的对齐是什么意思。如果你想要(uintptr_t)&amp;b % 8 == 0没有可移植的方式来获得它,因为从指针到整数的转换是实现定义的,不需要任何理智、自然的映射。

    如果您只想让缓冲区充分对齐以便使用uint64_t 类型进行访问,那么您的解决方案非常有效。但是你为什么不直接使用(可能是一个数组)类型struct buffer_with_alignment_requirement,而不是丑陋的联合黑客呢?换句话说,只需为缓冲区提供您想要访问它的正确类型即可。您可以将指向任何类型的指针传递给readfreadrevc 等以及您可能用于写入缓冲区的其他函数,即使您将其传递给一个函数需要 char *unsigned char * 类型的缓冲区指针,您可以在传递它时进行强制转换;这个演员表是完美定义和有效的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-08
      • 1970-01-01
      • 2017-02-23
      • 1970-01-01
      • 2012-11-23
      • 1970-01-01
      • 2013-10-10
      相关资源
      最近更新 更多