查询特定变量的对齐方式

37
C++11引入了alignas指示符来指定变量的对齐方式,以及alignof操作符来查询特定类型的默认对齐方式。但是,我没有找到获取特定变量对齐方式的方法。让我们看下面这个简单的例子:
alignas(16) float* array;

这是我们可以采取的措施:
  • alignof(float*) 返回 8,显然不是我们想要的。
  • alignof(array) 返回 16,正好是我们想要的,但这是编译器扩展;根据标准指定的 alignof 不能用于特定变量。
  • alignof(decltype(array)) 返回 8,这是可以预料的,但不是我们想要的。
  • std::alignment_of 是基于 alignof 实现的,因此没有太大帮助。
我想要一种机制来确认特定变量 array 是否在 16 字节边界上对齐。标准中是否有任何方法来执行这样的查询?

alignof 不需要提供这个 - 它在 编译时 返回一个最小的契约 - 你想要一个在 运行时 的值。特定的变量在运行时可能非常好地对齐到页面边界、兆字节边界或其他位置(即比承诺或要求的要好得多)。获取变量的地址并检查它是否可以被您所需的对齐值整除。 - tofro
我不太清楚类似于#define ALIGNED8(x)((&(x)&& 0x7)== 0)是否符合您的目的,或者为什么标准应该有重复这个简单结构的东西。 - tofro
你想知道某个变量的实际对齐方式或保证其最小对齐方式吗? - MikeMB
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - MikeMB
3
因为并非所有指针都只是内存地址。我使用过一台计算机,这种方法完全不适用(Prime迷你计算机。以字为寻址单位,并在一个扩展字中使用位偏移量——但位偏移量只能为0或8)。 - Martin Bonner supports Monica
显示剩余5条评论
3个回答

10

您可以尝试类似以下的方式:

bool is_aligned(const volatile void *p, std::size_t n)
{
  return reinterpret_cast<std::uintptr_t>(p) % n == 0;
}

assert(is_aligned(array, 16));
上述假设一个平坦的地址空间,且对 uintptr_t 执行的算术运算等同于对 char * 执行的算术运算。
尽管这些条件适用于大多数现代平台,但标准并不要求这两个条件都必须满足。
只要在将 void * 强制转换为 uintptr_t 时可以被反向转换回来 (参见什么是 uintptr_t 数据类型),实现可以执行任何转换。
更多细节请参见N4201(它提出了 is_aligned() 操作等其他内容)。

编辑

这里需要 volatile 吗?

它允许这样的东西:

alignas(16) volatile float a;

assert(is_aligned(&a, 16));

没有使用 volatile 会导致以下错误:

第一个参数没有已知的从 'volatile float *' 到 'const void *' 的转换

进一步参考:


如果需要这些保证,我可以理解为什么标准化函数会很有用。然而,N4201已被拒绝。 - Morwenn
@manlio,这里需要使用volatile吗?如果需要,那么它不应该是const void* volatile吗? - user5434231
@user5434231 我已经在回答中添加了一些参考资料。 - manlio
uintptr_t强制转换不需要进行任何转换即可使算术运算无效。考虑旧的8086地址模型,其中32位地址由16位段指针和16位偏移量组成,实际物理地址为segment<<4 + offset。 - Martin Bonner supports Monica

7

目前这个问题由 EWG 98 处理。我提交了一份文件

alignas说明符适用于对象,影响它们的对齐要求但不影响它们的类型。因此目前无法确定对象的实际对齐要求。该文件建议允许将 alignof 应用于对象和引用。

在此时此刻,你能做的最好的办法是定义一个单独的变量来保存变量的对齐方式。


1
很高兴看到其他人也考虑到这个问题并开始着手解决它 :) - Morwenn

3
您可以尝试这个方法:
template<size_t size, typename T>
constexpr bool IsAlignedAs(const T& v)
{
    return (reinterpret_cast<const size_t>(&v) % size) == 0;
}

std::cout << IsAlignedAs<16>(array) << std::endl;
std::cout << IsAlignedAs<32>(array) << std::endl;

顺便提一下,你可以使用 static_assert(IsAlignedAs<16>(array), "Array not aligned to 16"); 来检查你的数组是否对齐到16字节,但是你不能使用 static_assert(IsAlignedAs<32>(array), "Array not aligned to 32");,因为在编译时无法确定这个条件是否成立。 - Elijan9
不是clang的常量表达式。 - Jarod42
1
看起来确实是gcc特定的。在编译时检查地址的对齐方式(在将其链接到最终位置之前)似乎有点过早了... - Elijan9

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接