内存分配函数该怎么写?指针传递也发生拷贝?
需求:我们想写一个函数,这个函数用于内存的创建,返回指向内存的指针,这个函数该怎么写?
我们试着来写一下:
int *getMemory(int *ptr)
{
ptr = new int;
return ptr;
}
int main()
{
int *p = nullptr;
getMemory(p);
if (p == nullptr)
cout << "内存分配失败!" << endl;
else
{
cout << "内存分配成功!" << endl;
delete p;
}
return 0;
}
getMemory
函数传入int*
类型的指针,在函数体内创建内存,返回指向该内存的地址。看似函数写的没有错,运行之后,会输出:
内存分配失败!
显然,这个函数写错了,并且造成了内存泄漏,其实我们知道这个函数写的不对,但是为什么不对,又该怎么写呢?
让我们来测试另外一个用例:
int num1 = 4;
int num2 = 5;
void func(int *ptr)
{
ptr = &num2;
cout << *ptr << endl;
}
int main()
{
int *p = &num1;
cout << *p << endl;
func(p);
cout << *p << endl;
return 0;
}
上面的函数大体的意思为:有一个指针,指向num1
,通过函数func
,将指针指向了num2
,之后又输出了指针对应的值。因为指针p
在中途改变了指向,那么输出应为:4 5 5
。
然而输出为:4 5 4
。这又是为什么呢?
其实,当指针作为参数的时候,在传递指针变量的时候也发生了拷贝,跟值传递是一样的,它拷贝的就是这个指针,并且这个拷贝后的指针与原来的指针指向同一块内存。这就出现了两个指针同时指向一块内存的情况,一个是原指针,另一个是拷贝后的指针,拷贝后的指针在函数调用结束后就不存在了。
知道了以上的原理,就不难解释上面的问题了。
p
指针指向num1
,在调用函数的时候,p
指针发生拷贝,生成了ptr
,在函数体内,指针ptr
并没有操作原来指向的内存,而是又指向了另一块内存num2
,这时候ptr
与p
没有了关系,在函数调用结束之后,ptr
被销毁,p
指针还是指向原理的地址,并且没有进行任何操作,这就是输出4 5 4
的原因了。
让我们再来解决第一个问题,与第二个问题类似,在调用函数之后,p
指针发生了拷贝,生成了ptr
,ptr
又指向了另外被分配的区域,但它与p
就没关系了,所以在函数返回之后,指针p
依旧为nullptr
。
那正确的分配内存的函数该怎么写呢?
我们知道了传递指针会发生拷贝,被拷贝后的指针再指向其他的地址对原指针产生不了任何作用,那我们就应该让原指针与拷贝后的指针指向的依然是同一地址,那么拷贝后的指针再去指向别的区域,原指针也会同样指向该内存。
为了让它们的地址相同,所以要传递二级指针。
那正确的写法:
int *getMemory(int **ptr)
{
*ptr = new int;
return *ptr;
}
int main()
{
int *p = nullptr;
getMemory(&p);
if (p == nullptr)
cout << "内存分配失败!" << endl;
else
{
cout << "内存分配成功!" << endl;
delete p;
}
return 0;
}
上面的代码就会输出**内存分配成功!
**,这个函数就写正确了。
复盘一下,在函数中,我们传递了指针的指针,在发生拷贝后,两个指针第二级的地址是不同的,但第一级指针都同时指向了一块地址,这就使得内存分配成功了。
总结:
在传递指针的时候,指针也会发生拷贝动作,使两个指针指向同一内存区域,可以对该内存区域进行修改,但拷贝后的指针指向其他内存,与原指针就无关了。
要想正确的分配内存,需要传递二级指针,保证二级指针指向的一级地址是相同的。