C基础第41课 -- 内存操作经典问题分析一

学习自狄泰软件学院唐佐林老师C语言课程,文章中图片取自老师的PPT,仅用于个人笔记。


在这里插入图片描述

指针变量的值是非法的内存地址的含义:
非法的内存地址,不是给我们分配的内存地址。
那么什么是非法的内存地址呢? 除了合法的内存地址就是非法的内存地址。
合法的内存地址:
通过变量的定义而得到的变量地址是合法的。
通过malloc()申请得到的内存地址是合法的内存地址。
如果说我们的指针变量中保存的内存地址不属于上面两种情况,就可以说我们的指针变量这个时候就是野指针。

在这里插入图片描述

实验1

#include <stdio.h>
#include <malloc.h>


int main()
{
    int* p1 = (int*)malloc(40);
    int* p2 = (int*)1234567;//进行错误的强制类型转换。野指针
    int i = 0;
    
    /*
    已经发生了内存越界,由于指针运算产生了野指针
    改写了非法的内存地址,给我们使用的字节数只有40个字节,但是我们使用了
    不止40个字节那么多。所以由于指针运算所产生的野指针已经改写了内存其他
    地方不是给我们使用的位置。那么有人会奇怪,为什么没有产生段错误呢??
    因为这个程序很简单,只有一个malloc(),也就是说整个堆空间都是给我们使用的,
    所以即便我们改写了不是给我们使用的位置,也没产生问题,但是这样的做法是非法
    的。
    */
    for(i=0; i<40; i++)
    {
        *(p1 + i) = 40 - i;
    }

    free(p1); 
    
    for(i=0; i<40; i++)
    {
        p1[i] = p2[i];//使用已经释放的内存空间
    }
    
    return 0;
}

已经发生了内存越界,由于指针运算产生了野指针改写了非法的内存地址,给我们使用的字节数只有40个字节,但是我们使用了不止40个字节那么多。所以由于指针运算所产生的野指针已经改写了内存其他地方不是给我们使用的位置。那么有人会奇怪,为什么没有产生段错误呢??因为这个程序很简单,只有一个malloc(),也就是说整个堆空间都是给我们使用的,所以即便我们改写了不是给我们使用的位置,也没产生问题,但是这样的做法是非法的。

    for(i=0; i<40; i++)
    {
        *(p1 + i) = 40 - i;
    }

改:

#include <stdio.h>
#include <malloc.h>

int arr[40] = {1,2,3,4,5,6,7};
int main()
{
    int* p1 = (int*)malloc(40*sizeof(int));
    int* p2 = arr;
    int i = 0;
   printf("%p\n",p1);
    for(i=0; i<40; i++)
    {
        *(p1 + i) = 40 - i;
    }

    free(p1); 
    printf("%p\n",p1);
    for(i=0; i<40; i++)
    {
        p1[i] = p2[i];//使用已经释放的内存空间
    }
    
    return 0;
}

代码后面使用了已经释放的p1指针,也就是说使用了野指针,但是程序并没有报错,也是因为我们的程序很简单,只有一个malloc(),也就是说整个堆空间都是给我们使用的,所以即便我们改写了不是给我们使用的位置,也没产生问题,但是这样的做法是非法的,即便代码没有立即报错,但是我们的产品在使用一点时间后很可能会因为野指针出现意想不到的bug,而且这种BUG 很难追溯,因为距离野指针的产生使用 到bug 的出现已经过很久。

我们要将释放过后的指针立即赋值为空。这样在后面的程序中如果不小心使用这个已经能释放的野指针会立即报错使用空指针,提醒我们。

free(p1);
p1 = NULL;

在这里插入图片描述

实验2

#include <stdio.h>
#include <string.h>
#include <malloc.h>

struct Student
{
    char* name;
    int number;
};

char* func()
{
    char p[] = "D.T.Software";
    
    return p;//返回了局部数组的地址,返回了野指针。
}

void del(char* p)
{
    printf("%s\n", p);
    
    free(p);
}

int main()
{
	//没有初始化结构体变量,则结构体变量中的成员变量也没有初始化,指针没有初始化,就是野指针。产生了野指针
    struct Student s;

	//指针p 指向了函数返回的局部数组,野指针。
    char* p = func();
    //此时 p  name 都是野指针,使用野指针。
    strcpy(s.name, p); 
    
    s.number = 99;
    
    p = (char*)malloc(5);//5个字节
    //内存越界,产生内存越界的本质就是由于指针运算而得到了野指针,并且对野指针进行了操作。 
    strcpy(p, "D.T.Software");
    
    del(p);
    
    return 0;
}

当定义了初始化结构体变量,一定要初始化,如果结构体成员中有指针,并且没有初始化,那么就会产生野指针。

内存越界,产生内存越界的本质就是由于指针运算而得到了野指针,并且对野指针进行了操作

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux老A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值