C 语言迷途指针

17 Mar 2025 | 4 分钟阅读

与指针和内存管理相关的最常见的错误是悬空/野指针。有时程序员未能为指针初始化有效地址,那么这种未初始化的指针就被称为 C 语言中的悬空指针。

当对象被删除或从内存中释放而未修改指针的值时,在对象销毁时会出现悬空指针。在这种情况下,指针指向的内存已被释放。悬空指针可以指向包含程序代码或操作系统代码的内存。如果我们为此指针赋值,它将覆盖程序代码或操作系统指令的值;在这种情况下,程序将显示不期望的结果,甚至可能崩溃。如果内存被重新分配给另一个进程,那么解引用悬空指针将导致段错误。

让我们观察以下示例。

Dangling Pointers in C

在上面的图中,我们可以观察到指针 3 是一个悬空指针。指针 1指针 2 是指向已分配对象的指针,即对象 1 和对象 2。指针 3 是悬空指针,因为它指向已释放的对象。

让我们通过一些 C 程序来理解悬空指针。

使用 free() 函数释放内存。

在上面的代码中,我们创建了两个变量,即 *ptr 和 a,其中“ptr”是指针,“a”是整数变量。*ptr 是使用malloc()函数创建的指针变量。我们知道 malloc() 函数返回 void,所以我们使用 int * 将 void 指针转换为 int 指针。

语句int *ptr=(int *)malloc(sizeof(int)); 将分配 4 字节内存,如下面的图像所示

Dangling Pointers in C

语句free(ptr) 释放内存,如下图所示,其中有一个交叉符号,并且“ptr”指针由于指向已释放的内存而成为悬空指针。

Dangling Pointers in C

如果我们给“ptr”赋值 NULL,那么“ptr”将不再指向已删除的内存。因此,我们可以说 ptr 不是悬空指针,如下图所示

Dangling Pointers in C

变量超出作用域

当变量超出作用域时,指向该变量的指针将成为悬空指针。

在上面的代码中,我们执行了以下步骤

  • 首先,我们声明了名为“str”的指针变量。
  • 在内部作用域中,我们声明了一个字符变量。str 指针包含变量“a”的地址。
  • 当控制退出内部作用域时,“a”变量将不再可用,因此 str 指向已释放的内存。这意味着 str 指针变成了悬空指针。

函数调用

现在,我们将看到当调用函数时指针如何成为悬空指针。

让我们通过一个例子来理解。

在上面的代码中,我们执行了以下步骤

  • 首先,我们创建main()函数,在该函数中我们声明了“p”指针,它包含fun()的返回值。
  • 当调用fun()时,控制会转移到int *fun()的上下文,fun()返回变量“y”的地址。
  • 当控制返回到main()函数的上下文时,这意味着变量“y”不再可用。因此,我们可以说“p”指针是悬空指针,因为它指向已释放的内存。

输出

Dangling Pointers in C

让我们用图示表示上述代码的工作方式。

Dangling Pointers in C

让我们考虑另一个悬空指针的例子。

上面的代码与之前的代码类似,但唯一的区别是变量“y”是静态的。我们知道静态变量存储在全局内存中。

输出

Dangling Pointers in C

现在,我们用图示表示上述代码的工作方式。

Dangling Pointers in C

上面的图显示了堆栈内存。首先,调用fun()函数,然后控制转移到int *fun()的上下文。由于“y”是静态变量,所以它存储在全局内存中;它的作用域在整个程序中都可用。当返回地址值时,控制会返回到main()的上下文。指针“p”包含“y”的地址,即 100。当我们打印“*p”的值时,它会打印“y”的值,即 10。因此,我们可以说指针“p”不是悬空指针,因为它包含存储在全局内存中的变量的地址。

避免悬空指针错误

可以通过将指针初始化为NULL值来避免悬空指针错误。如果我们给指针赋值NULL值,那么指针将不再指向已释放的内存。将NULL值赋给指针意味着指针不指向任何内存位置。