【发布时间】:2020-01-17 23:34:24
【问题描述】:
在几篇关于如何确定void * 是否指向对齐内存的帖子中经常出现的解决方案之一涉及强制转换 void 指针。也就是说,我得到了一个void *ptr = CreateMemory(),现在我想检查ptr 指向的内存是否是某个值的倍数,比如16。
请参阅How to determine if memory is aligned?,它提出了具体声明。
指针 p 在 16 字节边界上对齐 iff ((unsigned long)p & 15) == 0。
类似的解决方案出现在这篇文章中。 How to check if a pointer points to a properly aligned memory location?
有人可以澄清一下,这个铸造是如何工作的?我的意思是,如果我有一个void *ptr = CreateMem();,在我看来(unsigned long)(ptr) 会给我指针本身,重新解释为unsigned long。为什么这个重新解释的 void 指针的值会对指向的内存的对齐有任何影响?
编辑:感谢所有有用的 cmets。请多容忍我一点。也许一个简化的例子可以帮助我更好地理解这一点。
#include <iostream>
using namespace std;
struct __attribute__((aligned(16))) Data0 {
uint8_t a;
};
struct Data1 {
uint8_t a;
};
int main() {
std::cout<<sizeof(Data0) << "\n"; //< --(1)
std::cout<<sizeof(Data1) << "\n";
Data0 ptr0[10];
Data1 ptr1[10];
std::cout<<(unsigned long) (ptr0 + 1) - (unsigned long) ptr0 << "\n"; //< --- (2)
std::cout<<(unsigned long) (ptr1 + 1) - (unsigned long) ptr1 << "\n";
return 0;
}
迄今为止,我总是将对齐内存解释为具有以下两个要求。 sizeof() 应该返回指定大小的倍数(参见条件 (1))。并且在增加指向对齐结构数组的指针时,步幅最终也将是指定大小的倍数(参见条件(2))。
所以我有点惊讶地看到ptr0 的实际值还有第三个要求。或者我可能完全误解了这一点。上例中ptr0 指向的内存是否会被认为是对齐的,而不是ptr1?
当我输入这个时,我意识到我并不真正理解对齐内存本身的含义。由于我在分配 cuda 缓冲区时主要处理此问题,因此我倾向于将其与我的数据结构所需的某种填充联系起来。
考虑第二个例子。 aligned_alloc 的那个。 https://en.cppreference.com/w/c/memory/aligned_alloc
分配 size 字节的未初始化存储,其对齐方式由对齐指定。
我不知道如何解释。说,我做了一个void *p0 = aligned_alloc(16, 16 * 2),与p1std::vector<char> buffer(32); char *p1 = buffer.data()相比,p0指向的内存有什么不同。
【问题讨论】:
-
指针只是一个包含内存地址的数字。您应该使用
uintptr_t而不是unsigned long,因为前者保证足够大以容纳任何地址 -
@AlanBirtles 我知道指针只是一个数字。但是为什么这个数字对指向的内存的对齐有任何影响呢?
-
如果数字是 16 的倍数,则它与 16 字节边界对齐
-
@AlanBirtles 我确定我在这里遗漏了一些 c++ 概念。但据我所知,我们有一个指针和内存指向。您声称的是指针值与它指向的内存的对齐方式有某种关系?它是否正确?这是 c++ 标准的一部分吗?
-
对我来说,听起来你要问的是对齐的定义。正如 16 字节对齐需要一个 16 倍数的地址,因为这首先是 16 字节对齐的含义。不过,感觉就像我错过了这个问题的一些东西。