C++-安全指针范围



我知道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++的一些实现可能并不都使用特定的值。但是你不能从语言定义中得到任何保证。

我将尝试对此给出一个大致的看法:

  1. 您可能永远不会访问真正的内存地址,因为每个现代操作系统都有并安装了多沙盒机制
  2. 从软件的角度来看,什么是NULL指针?NULL指针是一个指针变量,它存储一个值,程序员选择该值作为有意义的全值,该值用作标签,其含义如下"该指针无处可去"。根据定义,NULL指针不指向0x000000,NULL指针的定义不是指针指向哪里,而是这个名为NULL的宏的值,这个值将是这个NULL指针的值
  3. 在C中,你可以假设NULL == 0,只有在C中NULL是一个将NULL定义为等于0的int的宏,在C++中你没有这个自由
  4. 每个变量都有类型、标签和值(更好地说,是值的表示,而不是实值),至少对于基元值,指针也是如此,如果你说的是无效指针,你说的就是包含内存地址的指针(就像任何指针一样),而这些指针唯一的特殊之处在于,它们需要C++中的强制转换才能安全有效地解码;如果你认为void*是指向任何地方的指针,或者指向0NULL0x0000000的指针,那就大错特错了

顺便说一句,我仍然不明白你的问题。。。

现代操作系统可能会为NULL指针保留至少一个页面。因此0x1(或者0x4,如果您想要32位对齐)可能会起作用。

但请记住,C/C++语言并不能保证这一点。这样的行为必须依赖于操作系统和编译器。

此外,不能保证NULL指针的实际值。它可能全为零,也可能不全为零。如果不是,你的把戏就根本不会奏效。

最新更新