使用realloc操作数组时双指针的必要性探究
为什么手动内存管理中有时需要双指针,有时不需要?
核心原因很直接:C/C++里函数参数是值传递。如果你的函数需要修改外部指针变量本身的指向(比如让它指向realloc返回的新内存地址),就必须用双指针(指针的指针);如果只是用指针操作它指向的内存,单指针就够,但你的第二个函数其实是错误写法,只是碰巧“正常”而已。
双指针实现的正确逻辑
先看你的第一个函数:
struct pathToCity{ int lengthCities; char cities[100]; }; void appendToPathToCitiesArray(pathToCity **array, int *length, pathToCity newCity) { if (*length == 0) { *array = (pathToCity*) malloc(sizeof(pathToCity)); } else { *array = (pathToCity*) realloc(*array, (*length + 1) * sizeof(pathToCity)); } // 注意:这里应该检查*array是否为NULL,而非array本身 if (*array == NULL) { cout << "Error allocating memory." << endl; exit(-1); } (*array)[*length] = newCity; (*length)++; }
调用时传递的是&queue(指针的地址),函数里的*array就是外部的queue指针本身。realloc的行为分两种:如果原内存块后有足够空间扩容,就返回原地址;如果没有,就分配新内存、拷贝旧数据、释放旧内存后返回新地址。不管哪种情况,把realloc的结果赋值给*array,都会同步修改外部的queue指针,确保后续所有操作都在正确的内存地址上进行,这才是可靠的写法。
无双指针实现的“假象正常”
你的第二个函数本质是错误的,只是测试场景刚好没触发问题:
#include <iostream> using namespace std; struct shortText { int lengthText; char text[3]; }; void append(shortText *array, int *length, shortText newCity) { if (*length == 0) { array = (shortText*) malloc(sizeof(shortText)); } else { array = (shortText*) realloc(array, (*length + 1) * sizeof(shortText)); } if (array == NULL) { cout << "Error allocating memory" << endl; exit(-1); } array[*length] = newCity; (*length)++; } int main() { int length = 3; shortText *arr = (shortText*) malloc(sizeof(shortText) * length); arr[0] = {2, "ab"}; arr[1] = {2, "ab"}; arr[2] = {2, "ab"}; shortText s = {2, "ab"}; append(arr, &length, s); arr[3] = {8, "ji"}; cout << length << endl; cout << arr[3].lengthText << endl; free(arr); }
- 函数里的
array是外部arr指针的副本,函数内给array赋值realloc的结果,完全不会改变外部的arr指针。 - 你测试时
length初始是3,realloc尝试扩容到4个元素。如果操作系统在原内存块后面刚好有足够的空闲空间,realloc会直接原地扩容,返回的地址和原地址一样,这时候外部的arr指针还能访问到新空间,看起来正常。 - 但如果原内存块后面没有足够空间,
realloc会分配新内存,拷贝旧数据后释放旧内存。这时候函数内的array指向新地址,但外部的arr还是指向已被释放的旧地址:- 后续
arr[3]的赋值是访问无效内存,会触发崩溃; - 最后
free(arr)会导致二次释放旧内存,同样崩溃; - 新分配的内存地址只有函数内的
array知道,函数结束后就丢失了,造成内存泄漏。
- 后续
总结
- 只要你的函数需要修改外部指针变量的指向(比如初始化内存、重新分配内存),就必须用双指针,这样函数内部的修改才能同步到外部。
- 你的第二个函数只是运气好没出问题,实际项目中这种写法会导致各种难以排查的崩溃和内存泄漏,绝对不能用。
内容的提问来源于stack exchange,提问作者Markus
相关产品推荐
相关产品推荐

