C语言教程-C 语言中的悬空指针

与指针和内存管理相关的最常见的错误是悬空指针。有时程序员未能用有效的地址初始化指针,此类未初始化的指针在 C 语言中称为悬空指针。
悬空指针在对象销毁时出现,当对象被删除或从内存中释放时,未修改指针的值。在这种情况下,指针指向已被释放的内存。悬空指针可能指向包含程序代码或操作系统代码的内存。如果我们给这个指针赋值,它将覆盖程序代码或操作系统指令的值;在这种情况下,程序将显示不希望的结果,甚至可能崩溃。如果内存被重新分配给其他进程,那么解引用悬空指针将导致分段错误。
让我们通过一些 C 程序来理解悬空指针。
使用 free() 函数释放内存。
cCopy code
#include <stdio.h>
int main()
{
int *ptr = (int *)malloc(sizeof(int));
int a = 560;
ptr = &a;
free(ptr);
return 0;
}
在上面的代码中,我们创建了两个变量,即 *ptr
和 a
,其中 ptr
是一个指针,a
是一个整数变量。*ptr
是一个指针变量,它使用 malloc()
函数创建。由于我们知道 malloc()
函数返回 void
,所以我们使用 int *
将 void
指针转换为 int
指针。
语句 int *ptr = (int *)malloc(sizeof(int));
将分配 4 个字节的内存,语句 free(ptr)
释放了内存,如下图所示,带有一个交叉符号,ptr
指针变为悬空指针,因为它指向已释放的内存。如果我们将 NULL 值赋给 ptr
,那么 ptr
将不再指向已删除的内存。因此,我们可以说 ptr
不是悬空指针。
变量超出作用域
当变量超出作用域时,指向该变量的指针变成悬空指针。
cCopy code
#include<stdio.h>
int main()
{
char *str;
{
char a = 'A';
str = &a;
}
// a 超出作用域
// str 现在是一个悬空指针
printf("%s", *str);
}
在上面的代码中,我们执行了以下步骤:
- 首先,我们声明了指针变量
str
。 - 在内部作用域中,我们声明了一个字符变量。
str
指针包含变量a
的地址。 - 当控制流离开内部作用域时,变量
a
将不再可用,因此str
指向已释放的内存。这意味着str
指针变成了悬空指针。
函数调用
现在,我们将看到在调用函数时指针如何成为悬空指针。
让我们通过一个示例来理解。
cCopy code
#include <stdio.h>
int *fun()
{
int y = 10;
return &y;
}
int main()
{
int *p = fun();
printf("%d", *p);
return 0;
}
在上面的代码中,我们执行了以下步骤:
- 首先,我们在
main()
函数中创建了p
指针,它包含了fun()
的返回值。 - 当调用
fun()
时,控制流进入int *fun()
的上下文,fun()
返回y
变量的地址。 - 当控制流回到
main()
函数的上下文时,意味着变量y
不再可用。因此,我们可以说p
指针是一个悬空指针,因为它指向已释放的内存。
让我们考虑另一个悬空指针的示例。
cCopy code
#include <stdio.h>
int *fun()
{
static int y = 10;
return &y;
}
int main()
{
int *p = fun();
printf("%d", *p);
return 0;
}
上面的代码与之前的代码类似,唯一的区别是变量 y
是静态的。我们知道静态变量存储在全局内存中。
首先,调用 fun()
函数,然后控制流移动到 int *fun()
的上下文中。由于 y
是一个静态变量,所以它存储在全局内存中;它的作用域在整个程序中可用。当地址值被返回时,控制流回到 main()
的上下文中。指针 p
包含了变量 y
的地址,即 100。当我们打印 *p
的值时,它打印出变量 y
的值,即 10。因此,我们可以说指针 p
不是悬空指针,因为它包含了存储在全局内存中的变量的地址。
避免悬空指针错误
可以通过将指针初始化为 NULL
值来避免悬空指针错误。如果将 NULL
值赋给指针,那么指针将不指向已释放的内存。将 NULL
值赋给指针意味着指针不指向任何内存位置。