C语言的野指针危害真的很大吗?

简介: C语言的野指针危害真的很大吗?

大家除夕快乐,新年快乐。不敢祝愿大家代码无bug,但忠心祝大家代码少bug。直接进入正题,野指针危害真的很大,而且这种危害是不可估量的。设想一下你家里有个物体,不知什么时候突然出现,也不知什么时候突然消失。会把你的东西乱挪位置,还时不时打碎个瓶子。然而这个物体,在计算机的世界叫做野指针。在现实世界中,叫做猫。


下面举几个例子来再次说明野指针危害的重要性这个问题:


网友1:不小心写出了野指针, 不要说出来, 不要改, 自己默默记住位置. 这就是你下个月奖金的来源。


网友2:野指针危害不大,去年十一假期全组人加班啊,三倍工资的加班费啊!


网友3:从老师的视角来说,老师说:"再写野指针,不要说是我的学生"。


网友4:内存越界型的错误调试难度通常较高,因为程序通常不在错误发生时崩溃,而是在使用被覆盖的内容时以一定概率崩溃,此时通常已经离真正发生错误的地方很远。虽然有valgrind一类的设施帮助调试,但这类设施极大幅度增加运行开销,有时并不可用。


以上种种迹象表明,野指针的形成真的会产生很严重的后果和不可预测的灾难,然而指针是C语言的灵魂,你不认真花时间去琢磨他,还真的不行哦!

这篇文章我会从什么是野指针?野指针和垂悬指针的区别?野指针是如何形成的?怎么避免野指针?这四个问题展开来说细讲。


一.什么是野指针?

二.野指针和垂悬指针的区别?

三.野指针是如何让形成的?

四.怎么避免野指针?


一.什么是野指针?


野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的。通俗来讲就是这个指针无法正常使用,不知道指针指向哪里)。


指针变量的本质是值,这个特殊的值是一个内存地址值,而合法的内存地址包括定义的变量的地址(变量的地址存放在栈区)、malloc函数申请堆内存返回的地址(但未使用free释放,是在堆空间动态申请)。


需要注意的是,野指针不是NULL指针,通常,NULL指针可以使用if语句来判断,但是,C语言中没有任何方法用来判断一个指针是否为野指针!


二.野指针和垂悬指针的区别?


1.野指针:


一个无法正常使用,未知指向的指针,就是野指针。


2.垂悬指针:


首先指针是正常初始化的,当指针指向的对象被销毁时,指针如果没有被销毁,我们就说这个指针未置空,那么就成了垂悬指针,也叫悬空指针。

其实不难看出,这个垂悬指针是野指针的子集。


三.野指针是如何让形成的?


1.指针被释放(delete或free)后未置空:


指针被free或者delete之后,如果没有置为NULL,让人误以为p是个合法的指针。对指针进行free和delete,只是把指针所指的内存空间给释放掉,并没有把指针本身置空,此时指针指向的就是“垃圾”内存。这种指针就叫做野指针。释放后的指针应立即将指针置为NULL,防止产生野指针。


代码验证如下:

#include<stdio.h>
int main()
{
    //C语言风格
    int* p1 = NULL;//初始化指针
    int* p1 = (int*)malloc(sizeof(int) * 100);//C语言风格malloc对应free;
    free(p1);  //C语言风格
    p1 = NULL;//如果没有这一步置空操作,那么就会发生野指针;
    //C++风格
    int* p2 = NULL;//初始化指针
    p2 = new int[100];//C++风格new对应delete
    delete[]p2;  //C++风格
    p2 = NULL;  //如果没有这一步置空操作,那么就会发生野指针;
    system("pause");
    return 0;
}


注意:有些编译器是可以无差错的运行程序,表面上没什么问题,可是实际上程序已经崩溃了。


总结:我们在用malloc()开辟空间的时候,要检查返回值是否为空,如果为空,则开辟失败;如果不为空,则指针指向的是开辟的内存空间的首地址。指针指向的内存空间在用free()和delete释放后,如果程序员没有对其进行置空或者其他赋值操作的话,就会成为一个野指针


2.使用指针的时候没有对它进行初始化:


定义指针的时候没有对它进行初始化是最常见的错误,因为我们经常定义一些比如这样的一些变量int a;char c;并没有对变量a和c初始化,这样是合法的。但是,如果你这样:int *p; ,你要是这样就过去,不去初始化它,那么这时野指针就出现啦!


代码验证如下:


#include<stdio.h>
int maain()
{
    int* p1;
    printf("%p\n", p1);//编译可以通过,但是在运行的时候会出错
    return 0;
}


总结:针在被定义的时候,如果程序不对其进行初始化的话,它会随机指向一个区域,因为任意指针变量(出了static修饰的指针)它的默认值都是随机的。


3.指针所指向的变量在使用之前已经被销毁:


当指针指向某个对象之后,当这个对象的生命周期已经结束时,对象已经销毁之后,如果仍使用指针访问该对象,这时就会出现错误,造成野指针。

代码验证如下:


#include<stdio.h>
int* func()//func函数返回值类型是int*,是一个指针,所以返回值是返回一个地址
{
    int a = 10;
    return &a;
}
int main()
{
    int* p1 = NULL;
    p1 = func();
    printf("%p\n", p1);
    printf("%d\n", p1);
    system("pause");
    return 0;
}


输出结果如下:


d727478464605de075f96e9059b26bb2.png


最后一行,输出的并不是10,因为变量a是存储在栈空间的局部变量,离开函数函数体后就会被释放掉,因此输出的值就是不确定的值了。


4.指针指向的值出现越界:

#include<stdio.h>
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p = arr;
    for (int i = 0; i < 11; i++)//数组有10个元素,本来循环十次就可以遍历完数组,而事实上循环11次
    {
        printf("%d\t", *(p + i));//访问越界
    }
    return 0;
}

结果如下:


d3b5573fe8e739a01ce7e97e8e5b5eda.png

总结:不要返回指向栈内存的指针或者引用,因为栈内存在函数结束的时候会被释放。


四.怎么避免野指针?


以上例子可以说明野指针有时比较隐蔽,编译器不能发现,为了防止野指针带来的危害,使用指针的时候要注意以下几点:


1)在定义指针时,同时初始化为NULL;

2)在指针解引用之前,先判断这个指针是不是NULL;

3)在指针使用之前,将其赋值绑定给一个可用地址空间;

4)在指针使用完之后,将其赋值为NULL;

5)在循环里使用指针的时候注意检查是否越界;


本文章就分享到这里咯,再次祝大家新年快乐,阖家幸福!



2023.01.21

From:努力进大厂的新青年


相关文章
|
8小时前
|
安全 C语言
【C语言】如何规避野指针
【C语言】如何规避野指针
21 0
|
8小时前
|
C语言
C语言:数组和指针笔试题解析(包括一些容易混淆的指针题目)
C语言:数组和指针笔试题解析(包括一些容易混淆的指针题目)
|
8小时前
|
存储 程序员 编译器
爱上C语言:指针很难?来来来,看看这篇(基础篇)
爱上C语言:指针很难?来来来,看看这篇(基础篇)
|
8小时前
|
C语言
c语言指针总结
c语言指针总结
15 1
|
8小时前
|
C语言
C语言(指针详解)重点笔记:指针易错点,都是精华
C语言(指针详解)重点笔记:指针易错点,都是精华
2 0
|
8小时前
|
存储 C语言
C语言指针讲解(适用于初学者)
C语言指针讲解(适用于初学者)
6 0
|
8小时前
|
存储 程序员 C语言
【C 言专栏】C 语言指针的深度解析
【4月更文挑战第30天】C 语言中的指针是程序设计的关键,它如同一把钥匙,提供直接内存操作的途径。指针是存储其他变量地址的变量,通过声明如`int *ptr`来使用。它们在动态内存分配、函数参数传递及数组操作中发挥重要作用。然而,误用指针可能导致错误,如空指针引用和内存泄漏。理解指针的运算、与数组和函数的关系,以及在结构体中的应用,是成为熟练 C 语言程序员的必经之路。虽然挑战重重,但掌握指针将增强编程效率和灵活性。不断实践和学习,我们将驾驭指针,探索更广阔的编程世界。
|
8小时前
|
算法 搜索推荐 程序员
C语言中的函数指针和回调函数
C语言中的函数指针和回调函数
10 2
|
8小时前
|
存储 编译器 C语言
【C语言】初步解决指针疑惑
【C语言】初步解决指针疑惑
7 0
|
8小时前
|
存储 C语言
指针深入解析(C语言基础)带你走进指针,了解指针
指针深入解析(C语言基础)带你走进指针,了解指针