我正在阅读< Windows via C / C ++ 5th edition>以下是一些引用:
每个进程的虚拟地址空间都是 分成几个分区。在x86 32位上 Windows,0x00000000的分区 - 调用0x0000FFFF(包括) NULL-指针分配分区。 这个分区留出来帮助 程序员捕获NULL指针 分配。如果你的线程 aprocess尝试从或读取 写入此内存地址 分区,访问violoation是 提高。
我想知道为什么我们必须使用 范围 的地址空间而不是 只是值0 捕获NULL指针赋值? AFAIK,NULL等于0.那么这个设计背后的考虑是什么?用户不应该触摸此范围内的其他内容吗?或者NULL不一定等于0?
非常感谢。
答案 0 :(得分:4)
所有现代操作系统都巧妙地实现空指针解除引用检查,而不使用虚拟内存管理硬件执行开销。设置第0个4 KB页面(或者在这种情况下,第0-15页(总共64 KB)),因此任何数据读取或写入或地址0(或0x00000FFF或0x0000FFFF)的指令执行都会立即导致访问违规例外。
让我用不同的方式。将空指针解引用陷阱为0,但允许对(例如)地址1的数据引用将非常昂贵 - 因为您无法使用页面粒度VM硬件,所以有必要执行空指针比较和分支序列在很多指针解引之前。通过使第一页或前几页不可访问,可以在VM硬件中“免费”完成此检查。缺点是你不能将这些第一页用于其他任何内容。
(理论上,可以使用足够优化的编译器来确定哪些指针解引用不可能是空指针解引用,但是在出现这种聪明的编译器之前,未映射的0页面技巧将不得不这样做。)
那么为什么Windows(后Win98)空指针赋值分区为64 KB而不是最小必需的4 KB? Wyzard的答案也是捕获空指针数组索引错误的好例子。
*延迟更新:RE:为什么64 KB? - 我问了一个应该知道Windows Core团队的人,他们说这可能是一个64 KB的区域,因为Windows将某些内存管理结构保持在64 KB的分配粒度。这是为什么?我不知道,但也许Raymond Chen有答案:http://blogs.msdn.com/b/oldnewthing/archive/2003/10/03/55239.aspx。哇。 * 强>
答案 1 :(得分:3)
在C或C ++语言中,NULL始终等于0,但源代码中的0作为指针值并不一定对应于已编译二进制文件中的所有零位。但是,在Windows上我认为空指针实际上都是零位。
保留空间可能有助于捕获myarray[n]
为myarray
为空的mystruct->myfield
或mystruct
为{{1}}为空的内容。被访问的地址不一定是指针指向的地方,它可能在它之后的某个地方。
答案 2 :(得分:0)
该范围对于轻松检测由解除引用null和接近空指针引起的错误是必要的。例如:
int startOffset = 1000; //start at 1000th element;
char* buffer = obtain();// happens to be null
for( int i = startOffset; buffer[i] != 0; i++ ) {
//do stuff
}
注意在上面的示例中永远不会取消引用thet null指针。然而,由于buffer
是一个空指针,因此地址接近于空,并且可以使用“分区”技术轻松地解除引用它们。
答案 3 :(得分:0)
需要一个范围,因为当取消引用结构或类元素的指针时,访问的实际内存将是指针,加上结构中成员的偏移量。
struct Foo {
int a;
inb b;
}* Bar = 0;
Bar->b = 0;
在int = 32bit编译器中,'b'成员将是结构中的4个字节,因此Bar-> b将尝试访问地址0x00000004。