空指针的解应用操作(错误信息合集)
越界访问
首先我们上一个代码,看看这个的代码的问题
这个代码的问题显而易见 ,就是在循环里面,产生了越界访问的问题,这里你开辟了10个整形空间,但是从0-10一共是11个整形空间。导致访问不合法的空间,从而导致内存泄露,内存泄露会导致,这个内存一直使用,逐步积累,从而导致内存呗占满。
——————————————————————————————————————————
free只能释放动态的空间
上面的重点我们说的很清楚,为什么不能让指针移动
这里我们写上一个代码
指针移动之后 你指的是静态的空间,我们的是什么,我们的函数的动态内存空间的管理。
这里释放的是 p之后的空间
这里 的p不再是动态申请的起始位置
指针跑远了 也就是不指向释放空间了
所以才需要*(p+i)=i+1;
———————————————————————————————————————————
重复释放
也就是两个free
也就不行的
但是你要是 释放之后指向为空指针 那就没有什么问题
重复释放会导致的问题:
1. **不可预知的行为**:
当你重复释放同一块内存时,那个内存区域可能已经被重新分配给了其他用途。如果你再次试图访问它,你可能会读取到未定义的数据,或者更糟糕的是,破坏了其他部分的程序。
2. **内存冲突**:
如果你在释放内存之后又重新分配了相同的大小,并且期望得到相同的内存地址,这是不确定的。每次调用`malloc`或`realloc`都可能返回一个新的内存地址。因此,即使你的代码在逻辑上看起来可以工作,这也是依赖于实现的,并且不应该依赖这种行为。
3. **内存泄漏**:
虽然重复释放内存不会直接导致内存泄漏(因为内存已经被释放),但这通常意味着管理内存不当,可能会导致其他内存泄漏问题。
4. **性能问题**:
在某些情况下,操作系统和内存管理器可能会对重复释放小块内存的行为进行优化,如通过释放和立即重新分配来减少碎片。然而,这取决于具体的实现和配置,不应该依赖于此。
为了避免这些问题,你应该确保每个`malloc`或`realloc`调用都有一个对应的`free`调用,并且只释放一次。一个常用的做法是,在每次调用`malloc`或`realloc`时,都记录下返回的指针,然后在`free`中检查是否是同一个指针,避免重复释放。
———————————————————————————————————————————
忘记释放
导致内存泄露的问题
内存是一种资源 当申请之后不用的时候是需要回收的
其实malloc calloc realloc 申请的内促 如果不想使用 可以使用free释放
如果没有使用free来释放当程序运行结束的时候 也会由操作系统回收的
也就是 一直不退出的话 那个内存别人也用不上
所以 如果申请不释放的话 必须要进行说明
1.谁申请是谁释放------------------malloc free成对出现 --不然就会导致内存泄露的问题 --即使成对出现 也会导致泄露问题
就比如,这里也就是出问题了 即使malloc和free成对出现 也是出现问题了
2.如果不释放 也就是需要告诉使用的人,记得释放
———————————————————————————————————————————
传值调用错题详解(重点)
首先我们来看这个错误代码
首先我们看代码逻辑,首先main函数调用test,test接收的是void类型,设置一个指针变量,指向null,传递给get函数,也就是传递一个空指针给getmemory函数,这个函数接收了,然后进行运算。
接下来我们进行分析代码的错误逻辑。
为什么不取地址是错误的呢,
- 传递地址(指针的指针): 当你传递
&str
(str
的地址)给函数时,你是在告诉函数你想要操作的是str
变量的内存地址本身。这允许函数直接修改str
变量的值,或者使用str
变量的地址进行其他操作。 - 传递指针(不取地址): 当你传递
str
(指向char
的指针)给函数时,你是在告诉函数你想要操作的是str
指向的字符串。函数可以通过str
指针访问和修改字符串的内容,但不会直接修改str
指针本身的值。
这样会导致什么情况呢,就是内存销毁的问题,你这个函数传递的时候,传递的是指针,不是指针的地址。那么此时就会产生一种情况那就是,getmemory函数运算结束,这个函数进行了销毁。
因为他调用结束之后,函数会进行销毁,strcpy函数想拷贝到开辟的空间里面,OK!找不到空间了,因为进行了销毁。
什么意思呢,可以理解为,
张三,有一天忙碌完,不想回家了,在公司楼下开了个酒店住进去了,哎,很不错,告诉李四,我住的209房间特别好,你有空可以去试试。第二天李四也累了,那我也去了,我到地方我就记得209房间,但是张三的209房间已经退房了,那我肯定进不去,我非得进去,那就被保安打一顿。
你这里也是这样,你这里 想拷贝字符创,那可以没问题。关键点在于你这里传递的是数值,他不是地址,那数值的话,函数创建了,然后运行结束进行销毁。你自然找不到房间号。你这里要是传递的是空间的地址,那我可以直接去你的地址进行操作拷贝,你也不需要进行返回什么的,哪怕进行销毁了,那也没事。
1.因为这里传参传递的是数值,函数返回的时候进行了函数的销毁,从而导致非法访问
这里的意思 应该是把100个字节放到str里面去 但是传值调用是不行的 会导致有问题
正确的处理应该是什么呢
我们可以把str取地址,传参传的是地址,然后因为本身str已经是指针,我们采取用二级指针的方式进行接收,也就是指向的是指针的地址,
修改为正确代码1
此时就可以完成任务了
修改为正确代码2
当然 不喜欢二级指针 也可以进行数值的接受 也就是char*
找个数值进行接收,找个数值进行接收,那就是有返回类型,我们采取的返回类型是char*类型,因为这里是拷贝字符串。接收返回值也是一样的。
提示一下,所以其实不传参也是可以的
图解
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
动态管理内存题目详解
举例(传值调用问题)
动态内存管理-传值调用错题解析-CSDN博客
这里给一个代码进行分析
这里的错误很明显,在上面的传值调用问题详解里面,我们说的很清楚,这里我们重新复述一下 ,上面的链接是传址调用链接,我专门列出博客进行说明。可以反复观看
这个代码可能产生的问题是 有可能打印出来 也有可能打印不出来
为什么呢,我们看看代码的逻辑
首先调用函数,创建str,str=null,get调用,数组里面 放一个hello word,这个数组就产生了,此时返回数组,但是这里返回的是p,也就是数组首元素地址。这里返回首元素的地址,但是会产生的的问题就是,函数创建之后会进行销毁,你返回的是首元素的地址,这里空间进行了销毁,这个函数去找的时候,找不到这个地址。
下面进行图解:
假设这个地址 是这个
也就是 当传递数组 这里的空间已经释放
也就是返回的是空指针
也就是 这里是非法访问
当然 如果 空间 没有呗占用的时候 还能打印出来
但是不管有没有在占用 此时也都是非法访问
也就是
函数 这里是创建之后 返回之后 会进行销毁的
也就是销毁之后 这个函数 这个数组销毁了 也就是 有可能被覆盖 也可能不进行覆盖
也就是 这样的情况下 会出现野指针的情况
这里的情况也就是返回栈空间地址的问题
返回就是野指针
———————————————————————————————————————————
举例(返回变量问题)
这里也是返回栈空间地址的问题
我们进行逻辑代码的分析
首先我们进入main函数,进入函数里面调用test,用指针p进行接收,然后我们进入test函数,n赋值等于10,此时返回数值。
这里错误就错在返回的是地址,之前我们说到过,地址是会进行销毁的,函数创建之后,返回的时候进行销毁,还是那个找酒店的例子。我们是找不到的。
但是 返回局部变量是可以的
但是 返回局部空间的地址是不可以的 因为地址会进行销毁的
这两句话什么意思呢,就是返回一个数值是可以的,比如返回n,但是返回n的地址是不行的,n是局部变量,局部变量在函数销毁的时候是一并销毁的。
———————————————————————————————————————————
举例(忘记free问题)
这里没有任何问题 但是 唯一的问题 就是没有
free
需要 free一下 指向空指针一下
即使这个程序没有free但是 会自动free
但是 万一程序不退出
那就是大问题
也就是 空间溢出问题,程序一直占用,得不到释放,从而导致内存占用满了,从而导致崩盘。
解决办法就是只需要在最后加上free(str);进行内存空间的释放
———————————————————————————————————————————
举例(忘记置为空指针问题)
这个代码的问题是, 提前释放
也就是这里拷贝之后就还给操作系统,我们还没有判断和打印,就已经进行销毁创建的空间了,导致的错误
也就是我们没有使用权限了
但是这一块空间 还在
也就是开酒店 虽然你退房了
但是房间还在
但是 虽然我这个空间 还在 但是 我没有使用空间
实际修改代码
也就是 free使用之后,置为空指针 这样后面也就没有什么意义了,这里才是考察的知识点
你也可以修改为这个free放到最后进行释放,这个也是没有问题的 ,但是这个不是我们的考察点。