是否有一种可移植的(C++ 标准)方法来计算前一个对齐的指针?

Posted

技术标签:

【中文标题】是否有一种可移植的(C++ 标准)方法来计算前一个对齐的指针?【英文标题】:Is there a portable (C++ standard) way to compute the previous aligned pointer? 【发布时间】:2021-09-16 15:33:51 【问题描述】:

给定alignas(N) char buffer[N];和[&buffer[0]&buffer[N-1]]之间的char* p,我可以通过使用pN获得指向buffer[0]的指针吗?

char* previous_aligned_pointer(char* ptr, size_t align) 
   // how?


int main() 
  constexpr int N = 32;
  alignas(N) char buffer[N];
  assert(previous_aligned_pointer(&buffer[rand() % N], N) == &buffer[0]);

我想在可移植的 C++ 中执行此操作(如果可能),因此可能无法使用诸如将指针转换为 uintptr_t 然后对其执行算术之类的操作。

【问题讨论】:

即使您执行alignas(N) char buffer[N * 2];&buffer[rand() % (N * 2)],您是否希望它能够正常工作,或者缓冲区大小总是小于对齐方式? 我很好奇这种功能的用例。你能分享一下你期望这个函数解决的问题吗? 这听起来像是一个 XY 问题。你到底想解决什么问题? 【参考方案1】:

有一个标准库函数可以获取 next 对齐的指针。它被称为std::align。然后你可以减去对齐得到前一个。

一般来说,可能会在缓冲区之外进行指针运算,在这种情况下,减法在技术上将是 UB。如果这是一个问题,那么如果您可以在缓冲区中分配额外空间以便下一个对齐的指针始终在缓冲区内,那么您可以避免使用 UB。

唯一的其他方法是自己实现它,这将不得不依赖转换为依赖于实现依赖的无保证行为的整数类型。结论:不,我不认为有一个通用的、严格符合标准的解决方案。


但是,对于您的示例情况,没有问题。下一个对齐的地址是指针越过末尾,所以减去 N 可以得到指向缓冲区开头的地址。

【讨论】:

如果您将&buffer[0] 传递给std::align 怎么办?是否会返回&buffer[0],在这种情况下减法不正确? 但是您传递的 space 参数比它应该传递给 std::align 的参数更大。结果指针指向缓冲区外。 在这种特殊情况下,std::align 产生的指针将指向缓冲区的末尾。这仍然是一个有效的(尽管不可取消引用)指针。 @NateEldredge 这可能会有所帮助,但仍然存在传递space 参数的问题,据我了解,这必须是p 和之后可寻址的多个字节,在其他单词p 和缓冲区末尾之间的空格。这意味着您已经知道缓冲区的结尾。我不确定在space 上撒谎的后果是什么,但如果它太低,则该功能不会给出正确的结果,如果它太高,则可能是 UB。 @FrançoisAndrieux 技术标准要求ptr represents the address of contiguous storage of at least space bytes。您可以通过使用 1 的 space 并再次递增地调用它来避免该问题,直到找到下一个对齐或 space 到达 N 在这种情况下原始指针已对齐。这很愚蠢,但它应该可以工作。

以上是关于是否有一种可移植的(C++ 标准)方法来计算前一个对齐的指针?的主要内容,如果未能解决你的问题,请参考以下文章

是否有一种可移植的方式来定义 INT_MAX?

是否有一种可移植的方式来获取 Python 中的当前用户名?

是否有一种可移植的方式在 Makefile 中制作可移植的可选依赖项?

是否有一种标准方法来捕获适用于 linux 和 windows 的 c++ 应用程序中的键盘输入?

使用 testthat 在单元测试中重用变量。是不是有一种可接受的做法来避免重新创建计算量大的变量

有没有一种可移植的方法来设置flock() 的超时时间?