c++ - 铸造一个空指针来检查内存对齐
问题描述
几个帖子中经常出现的解决方案之一是关于如何确定void *
指向对齐内存的点是否涉及强制转换 void 指针。也就是说,我得到一个void *ptr = CreateMemory()
,现在我想检查指向的内存是否ptr
是某个值的倍数,比如 16。
请参阅如何确定内存是否对齐?这提出了一个具体的要求。
指针 p 在 16 字节边界上对齐 iff ((unsigned long)p & 15) == 0。
类似的解决方案出现在这篇文章中。 如何检查指针是否指向正确对齐的内存位置?
有人可以澄清一下,这个铸造是如何工作的?我的意思是,如果我有一个void *ptr = CreateMem();
,在我看来,(unsigned long)(ptr)
指针本身会被重新解释为一个unsigned long
. 为什么这个重新解释的 void 指针的值会对指向的内存的对齐有任何影响?
编辑:感谢所有有用的评论。请多容忍我一点。也许一个简化的例子可以帮助我更好地理解这一点。
#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 字节的未初始化存储,其对齐方式由对齐指定。
我不确定如何解释这一点。说,我做了一个,与说where相比,void *p0 = aligned_alloc(16, 16 * 2)
指向的内存有什么不同。p0
p1
std::vector<char> buffer(32); char *p1 = buffer.data()
解决方案
为什么这个重新解释的 void 指针的值会对指向的内存的对齐有任何影响?
因为在普通架构中,void 指针到 unsigned long 的转换是指针指向的内存地址。或者由于转换为无符号类型的工作方式,如果地址的低位太大而无法放入无符号长整数中。测试对齐就足够了,因为在这种情况下,您只需要地址的最低位。
我不确定它是否真的是每个标准都规定的,因为如果将内存地址的实际表示留给实现,我可以记住 16 位英特尔处理器使用的段+地址表示。在那些架构中,可寻址内存使用 20 位,而远指针(能够表示任何地址)表示为 32 位无符号值,其中高 16 位是段,低 16 位是偏移量。然后将它们组合为:
Segment S S S S _ (4 * 4 = 16 bits)
Offset _ O O O O (4 * 4 = 16 bits)
Address A A A A A (5 * 4 = 20 bits)
您可以看到这种架构不允许轻松测试高于 16 的对齐。例如,这个指针值 0x00010040 可以被 64 整除,但实际地址是 0x00050 (80),只能被 16 整除。
但是只有恐龙才能记住 Intel 8086 及其分段地址表示,即使使用它,测试最多 16 字节的对齐也很简单......
推荐阅读
- swiftui - SwiftUI - 从网络接收数据后导航
- python - Spark:如何从具有属性的多个嵌套 XML 文件转换为 Data Frame 数据
- javascript - Next.js 不加载 jsx
- c - 运行 libserialport (list_ports) 时出错,sp_list_ports() 失败
- javascript - 我的 AJAX 调用函数对于删除表格行是否正确?
- r - R:搜索使用其系数(phi_1 和 phi_2)模拟 AR(2) 的正确种子
- javascript - 用于附加 xml 节点的 Groovy 脚本代码需要 15 多个小时才能获得 6K 员工记录
- docker - docker-compose 变量格式
- python - 如何使用用户输入创建类实例?
- php - 如何删除品牌 WHMCS Ver 8.1“由 WHMcomplete 解决方案提供支持”