#include <iostream>
int main(int argc, char** argv) {
int* heap_var = new int[1];
/*
* Page size 4KB == 4*1024 == 4096
*/
heap_var[1025] = 1;
std::cout << heap_var[1025] << std::endl;
return 0;
}
// Output: 1
在上面的代码中,我在堆中分配了4个字节的空间。现在,当OS将虚拟内存映射到页面中的系统内存(每个4KB)时,我的虚拟mems堆中的4KB块将被映射到系统内存。为了测试,我决定尝试访问我分配的页面/堆块中的其他地址并且它有效,但是我不应该被允许从一开始就访问超过4096个字节(这意味着索引1025作为一个int变量是4字节)。
我很困惑为什么我能够从堆块的开头访问4 * 1025字节(超过已分配页面的大小)而不会出现seg错误。
感谢。
答案 0 :(得分:2)
平台分配器可能分配的页面大小远远超过页面大小,因为它计划使用该内存“桶”进行其他分配,或者可能在那里保留一些内部状态,很可能在发布版本中存在远远超过只是一个页面大小的虚拟内存块。你也不知道那个特定页面在哪里分配了内存(你可以通过掩盖一些比特来找到)并且没有提到平台/拱门(我假设x86_64
)没有告诉这个页面甚至是4kb,它可能是2MB“巨大”的页面或类似的东西。
但是,通过访问外部数组边界,您将触发未定义的行为,如果在写入情况下发生读取或数据损坏,则会发生崩溃。
请勿使用您不拥有的内存。
我还应该提一下,这可能与C ++无关,因为new[]
运算符通常只是在核心平台库的幕后调用malloc
/ calloc
({{1在OSX或libSystem
或glibc
或Linux上的任何其他内容,甚至是拦截分配器)。您遇到的段错误通常来自堆块周围的保护页面,或者在没有保护页面的情况下使用未映射的内存。
注意:请勿在家中尝试:有些情况下,您可能会故意触发一般会被视为未定义行为的内容,但在该特定平台上可能确切知道那里有什么(一个很好的例子就是在Linux上滥用musl
opaque以获得pthread_t
而没有额外系统调用的开销,但你必须确保使用正确的libc,右边构建该libc的类型,该libc的正确版本,使用它构建的正确编译器等)。