【问题标题】:Does dereference a NULL pointer guarantee to crash a program in C/C++?取消引用 NULL 指针是否保证 C/C++ 中的程序崩溃?
【发布时间】:2014-04-03 19:48:27
【问题描述】:

我遇到了这个有线代码,它没有崩溃。

#include <stdio.h>
struct s
{
    char* c;
    char* c2;
};

int main()
{
    struct s* p = NULL;
    printf("%d\n", &(p->c));
    printf("%d\n", &p->c2);
    printf("%d\n", &(*p).c2);
    return 0;
}

输出:

0
4
4

我有几个问题无法回答:

  1. 在c/c++中NULL指针总是等于0吗?

  2. 如果 0 恰好是变量的地址会怎样?

  3. 似乎输出是结构成员的偏移地址。这是如何计算的。我的意思是 p->c 是 c 的地址,它不存在,因为 p == NULL。如果c的地址不存在,怎么能通过&p->c获取c的地址?

  4. 在 C/C++ 中取消引用 NULL 指针会保证程序崩溃吗?(假设在系统中)

【问题讨论】:

  • 在 C++ 中 null 被替换为 0。
  • 所有 3 个printf() 都是未定义行为 (UB)。 "%d" 是指针的错误匹配说明符。使用"%p"

标签: c++ c pointers struct null


【解决方案1】:

不,取消引用空指针并不能保证崩溃。

一方面,在许多情况下,编译器能够优化解引用操作。在您的情况下,&amp;(p-&gt;c) 名义上取消引用空指针,但您随后获取结果的地址(在选择指向结构的成员之后)。大概编译器将整个表达式替换为一个数字,这是结构内成员的偏移量。 (不能保证空指针指向地址 0,或者它会以这种方式进行优化,但它恰好在您的系统上这样做。)

这是标准offsetof 宏的通用实现的基础。它不能在可移植的标准 C 或 C++ 中实现,但实现通常使用像这样的特定于系统的技巧——只要它在该系统上产生正确的结果,这是完全有效的。 p>

其次,即使您实际上取消引用空指针,行为也是未定义。这可能意味着您的程序会崩溃,但就语言标准而言,它实际上可以做任何事情。

过去有一些系统(可能现在仍然存在)空指针指向地址 0,并且系统的内存映射设置为使地址 0 是恰好包含 0 值的有效内存地址.例如,在这样的机器上,空指针的作用类似于指向以'\0' 结尾的空字符串的指针。当在此类系统上工作且无意依赖于该行为的代码被移植到具有更强内存保护的新系统时,这导致了一系列错误修复。 (我认为这可能是从基于 68K 的 Sun 3 到基于 SPARC 的 Sun 4 的过渡,但我不确定。)

有关空指针的更多信息,请参阅comp.lang.c FAQ 的第 5 节。其中大部分也适用于 C++。

【讨论】:

    【解决方案2】:

    您实际上并没有取消引用指针。这就是代码不会崩溃的原因。代码所做的是获取结构成员的偏移量。第一个成员的偏移量为 0 字节,第二个成员的偏移量为 4 字节。

    c 标准不保证 NULL 指针被定义为 0,它是实现定义的。 (201 倍:7.19)

    【讨论】:

    • 据我了解, p->c 是取消引用它, &(p->c) 是获取地址。
    • @ycshao 您必须将表达式视为一个整体,它不会取消引用。
    • p-&gt;c 定义为(*p).c,参见[expr.ref]/2。这是一个取消引用操作:类型“指向T”的表达式被转换为类型为T 的表达式。我猜您将“取消引用”与“取消引用并对结果执行左值到右值转换”混为一谈。
    【解决方案3】:

    在 C++ 中,空指针始终为 0。

    在 C 中,NULL 指针可能不是 0,但我从未见过这样的系统。

    在所有使用虚拟内存取消引用空指针的现代系统上都会崩溃。但顺便说一句,你没有在你的程序中这样做。

    【讨论】:

    • 文字 0 是一个空指针常量。这并不意味着空指针被表示为全位为零。
    【解决方案4】:
    1. NULL 始终为零:http://www.cplusplus.com/reference/cstring/NULL/

    空指针常量是一个整数常量表达式,其计算结果为零(如 0 或 0L),或将此类值强制转换为 void* 类型(如 (void*)0)。

    2。这是极不可能的,部分原因是虚拟内存,并且可能在编译器中被阻止。 3. 编译器可能会实现带有偏移量的结构。此外,您没有取消引用指针,只是获取地址。

    【讨论】:

      【解决方案5】:
      1. 当然,NULL 始终为 0。除非您将其定义为其他值。
      2. 我认为现在在0映射一个页面是不允许的,但是如果允许,你可以在地址0有一个变量。
      3. 您看,您从未真正访问过 p 指向的变量。你只想要他们的地址。它们可以从 p 计算出来。一个指针占用 4 个字节(在您的平台上),所以我们知道结构偏移量中的第一个指针是 0,第二个是 4,等等。

      【讨论】:

      • NULL 扩展为常量 0,但这并不意味着空指针指向内存中的地址零。请参阅comp.lang.c FAQ 的第 5 节;其中大部分也适用于 C++。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-16
      • 2020-05-12
      • 2020-05-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多