| 下一篇 |
|---|
| 因外部晶振频率改变导致 MCU 锁死的原因及处理方法 |
目 录
- 函数内可以释放内存,却无法将外部指针置 NULL?
- 1)现象
- 2)原因
- 3)正确做法
函数内可以释放内存,却无法将外部指针置 NULL?
1)现象
先从一段串口环形缓冲区释放函数的原版实现开始:
int32_tring_buffer_release(volatileRING_BUFFER_T*ring_buffer){if(!ring_buffer||!ring_buffer->buffer)return-1;free(ring_buffer->buffer);ring_buffer->buffer=NULL;free((void*)ring_buffer);ring_buffer=NULL;// ← 这行置空无效return0;}调用处类似:
RING_BUFFER_T*rb=ring_buffer_init(256);// 使用 rb ...ring_buffer_release(rb);// rb 仍然是非 NULL 的悬空指针!这段代码能成功释放堆内存,但写ring_buffer = NULL无法让调用方的rb变成NULL。这是为什么?
2)原因
核心原因:指针变量的值传递。
C 语言所有函数参数都是按值传递。当我们将指针变量传给函数时,传递的是指针变量里保存的地址值,而不是指针变量本身。
示意图如下:
调用方: rb (变量) → 存储值 0x20001000 (指向结构体内存) ↓ 函数形参: ring_buffer (另一个局部变量) → 也存储值 0x20001000 ↓ 通过该地址操作内存 堆内存块 (0x20001000)free(ring_buffer)依靠地址值释放内存,无论传入的地址是原始变量还是副本,都能正确定位堆内存。因此释放必然成功。- 但
ring_buffer = NULL只把函数内部那个副本局部变量的值改成了NULL,完全不影响外部rb变量,它仍然保留旧的地址值,变成悬空指针。
3)正确做法
正确做法:用二级指针修改调用方指针。
如果希望函数有能力将外部指针安全置空,必须传入原指针的地址(二级指针):
int32_tring_buffer_release(volatileRING_BUFFER_T**pp_ring_buffer){if(!pp_ring_buffer||!*pp_ring_buffer)return-1;volatileRING_BUFFER_T*rb=*pp_ring_buffer;if(rb->buffer){free(rb->buffer);rb->buffer=NULL;}free((void*)rb);*pp_ring_buffer=NULL;// 通过解引用修改外部的指针变量本身return0;}调用方式变为:
ring_buffer_release(&rb);// 此时 rb 被置为 NULLpp_ring_buffer接收到的是rb变量的地址,*pp_ring_buffer就是rb变量本身,对它赋值直接改变了main中的rb。