我知道NULL
(0x00000000)是一个指向零的指针,因为操作系统不允许进程在这个位置分配任何内存。但是,如果我使用0x00000001(幻数或代码指针),那么可以安全地假设操作系统不允许在这里分配内存吗?如果是这样的话,那么在哪里可以安全地假设呢?
标准(第一个)
该标准仅保证0
是指针所能达到的哨兵值。根本无法保证底层的内存表示;它是由实现定义的。
除了读取指针状态或写入新状态(包括解引用或指针算术)之外,将指针设置为该sentinel值是未定义的行为。
虚拟内存
在虚拟内存时代(即,每个进程都有自己的内存空间,独立于其他进程),在进程内存空间中,空指针通常表示为0。实际上,我不知道有任何其他架构,尽管我认为在大型机中可能不是这样
Unix
在Unix世界中,通常会为null值保留0x8000以下的所有地址空间。内存没有分配,实际上,它只是受到保护(即,置于特殊模式下),这样,如果你试图读取或写入它,操作系统就会触发分段故障。
使用这样一个范围的想法是,不一定要按原样使用空指针。例如,如果您使用一个为空的std::pair<int, int>* p = 0;
,并调用p->second
,则编译器将执行指向second
(即通常为+4
)所需的算术运算,并尝试直接访问0x4
处的内存。数组显然使问题更加复杂。
在实践中,这个0x8000
限制应该足够实用,可以检测大多数问题(并避免内存损坏或其他问题)。在这种情况下,这意味着您可以避免未定义的行为,并获得"正确"的崩溃。然而,如果你使用的是一个大数组,你可能会超过它,所以这不是一个银弹。
您的实现或编译器/运行时堆栈的特定限制可以通过文档或连续的试验来确定。甚至可能有一种方法可以调整它。
您不应该假设指针的实际值。特别是,空指针是,而不是需要用零地址表示,即使字面0
看起来确实像零。
唯一有效的范围应该是操作系统分配给您的范围。操作系统应该拒绝其他范围。
该规则的一个例外是共享内存。
C++标准不"保留"除零(null)以外的任何指针地址。因此,使用1或任何其他值作为"魔术"指针值是不安全的。当然,在实践中,c++的一些实现可能并不都使用特定的值。但是你不能从语言定义中得到任何保证。
我将尝试对此给出一个大致的看法:
- 您可能永远不会访问真正的内存地址,因为每个现代操作系统都有并安装了多沙盒机制
- 从软件的角度来看,什么是NULL指针?NULL指针是一个指针变量,它存储一个值,程序员选择该值作为有意义的全值,该值用作标签,其含义如下"该指针无处可去"。根据定义,NULL指针不指向0x000000,NULL指针的定义不是指针指向哪里,而是这个名为NULL的宏的值,这个值将是这个NULL指针的值
- 在C中,你可以假设
NULL == 0
,只有在C中NULL是一个将NULL定义为等于0的int的宏,在C++中你没有这个自由 - 每个变量都有类型、标签和值(更好地说,是值的表示,而不是实值),至少对于基元值,指针也是如此,如果你说的是无效指针,你说的就是包含内存地址的指针(就像任何指针一样),而这些指针唯一的特殊之处在于,它们需要C++中的强制转换才能安全有效地解码;如果你认为
void*
是指向任何地方的指针,或者指向0
、NULL
或0x0000000
的指针,那就大错特错了
顺便说一句,我仍然不明白你的问题。。。
现代操作系统可能会为NULL指针保留至少一个页面。因此0x1(或者0x4,如果您想要32位对齐)可能会起作用。
但请记住,C/C++语言并不能保证这一点。这样的行为必须依赖于操作系统和编译器。
此外,不能保证NULL指针的实际值。它可能全为零,也可能不全为零。如果不是,你的把戏就根本不会奏效。