2

我知道NULL(0x00000000) 是一个空指针,因为操作系统不允许进程在这个位置分配任何内存。但是,如果我使用 0x00000001(幻数或代码指针),是否也可以安全地假设操作系统不允许在此处分配内存?如果是这样,那么直到哪里可以安全地假设?

4

6 回答 6

4

标准(第一)

就指针而言,该标准仅保证这0是一个标记值。无法保证底层内存表示;它的实现定义。

除了读取指针状态或写入新状态(包括取消引用或指针运算)之外,使用设置为该标记值的指针是未定义的行为

虚拟内存

在虚拟内存时代(即,每个进程都有自己的内存空间,独立于其他进程),空指针在进程内存空间中最常表示为 0。我实际上不知道任何其他架构,尽管我想在大型机中可能不是这样。

Unix

在 Unix 世界中,通常会为空值保留 0x8000 以下的所有地址空间。内存没有分配,实际上,它只是受到保护(即,置于特殊模式下),因此如果您尝试读取或写入它,操作系统将触发分段错误。

使用这样一个范围的想法是空指针不一定按原样使用。例如,如果您使用std::pair<int, int>* p = 0;为 null 的 a 并调用p->second,则编译器将执行指向second(即+4通常)所需的算术并尝试0x4直接访问内存。数组显然使问题更加复杂。

在实践中,这个0x8000限制应该足够实用以检测大多数问题(并避免内存损坏或其他问题)。在这种情况下,这意味着您可以避免未定义的行为并获得“适当的”崩溃。但是,如果您使用大型阵列,您可能会超过它,所以它不是灵丹妙药。

您的实现或编译器/运行时堆栈的特定限制可以通过文档或连续试验来确定。甚至可能有一种方法可以对其进行调整。

于 2012-10-07T13:04:39.400 回答
2

您不应该对指针的实际值做任何假设。特别是,空指针不需要用零地址表示,即使字面量0看起来像零。

于 2012-10-07T12:09:58.627 回答
1

C++ 标准不“保留”除零(空)以外的任何指针地址。因此,使用 1 或任何其他值作为“魔术”指针值是不安全的。当然,在实践中,c++ 的某些实现可能并非每个都使用某些值。但是您无法从语言定义中得到任何保证。

于 2012-10-07T12:12:39.737 回答
1

唯一有效的范围应该是操作系统分配给您的范围。其他任何事情都应该被操作系统拒绝。

该规则的一个例外是共享内存。

于 2012-10-07T12:09:05.613 回答
0

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

但请记住,C/C++ 语言不能保证这一点。您将不得不依赖您的操作系统和编译器来实现此类行为。

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

于 2012-10-07T12:41:02.507 回答
0

我将尝试对此给出一个广泛的看法:

  1. 由于每个现代操作系统都拥有并实施了多重沙盒机制,您可能永远不会访问真实的内存地址。
  2. 从软件的角度来看,什么是 NULL 指针?NULL 指针是一个指针变量,它存储程序员选择的值作为有意义的值,并且该值用作具有以下含义的标签“此指针无处可去”。根据定义,NULL 指针不指向 0x000000,NULL 指针的定义与该指针指向的位置无关,而是该宏的值称为 NULL,并且该值将是该 NULL 指针的值。
  3. 在 C 中你可以假设NULL == 0,只有在 C 中 NULL 是一个将 NULL 定义为等于 0 的 int 的宏,在 C++ 中你没有这个自由
  4. 每个变量都有类型,标签和值(更好的说法是值的表示不是真实值),至少对于原始值,指针也是如此,如果您在谈论 void 指针,您正在谈论包含的指针一个内存地址(就像任何指针一样),这个指针唯一的特别之处是它们需要在 C++ 中进行强制转换才能安全有效地解码;如果您将void*其视为指向无处或指向0或指向NULL或指向的指针,那将是一个很大的错误0x0000000

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

于 2012-10-07T12:26:31.757 回答