C++ 传递动态内存

最近在看程序员面试宝典的书中遇到了传递动态内存的问题

#include <iostream>
using namespace std;
void GetMemory(char *p,int num)
{
    p=(char *)malloc(sizeof(char) * num);
}
void Test(void)
{
    char *str=NULL;
    GetMemory(str,100);    //str仍然为NULL
    strcpy(str,"hello");//运行错误
}
int main()
{
    Test();
    return 0;
}

书上的解释是:在函数GetMemory(char *p,int num)中,*p实际上是主函数中str的一个副本,p申请了内存,只是把p指向的内存地址改变,而str并没有改变,所以str依然没有获得内存,在执行字符串复制时就会出错。而且每次p申请的内存都不会得到释放,最终会造成内存泄露。

不胜理解,查找相关资料发现是值传递和地址传递的问题。

在C语言中函数参数的传递有:值传递,地址传递,引用传递这三种形式。

1、值传递:形参是实参的拷贝,改变形参的值并不会影响外部实参的值。从被调用函数的角度来说,值传递是单向的(实参->形参),参数的值只能传入,不能传出。当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递。

典型代码:

#include <iostream>
using namespace std;
void swap(int p,int q)
{
    int temp;
    temp=p;
    p=q;
    q=temp;
    //cout<<p<<""<<q<<endl;
}
int main()
{
    int a=1,b=3;
    swap(a,b);
    cout<<a<<endl;
    cout<<b<<endl;
     return 0;
}

最后输出的结果a=1,b=3;可以看到a、b的值并没有改变,实参a、b在传递参数给形参p、q的时候,本身并不会改变,最后形参的值不会再重新传回给实参,改变的是形参p、q的值,实参a、b不会发生任何改变。注意在函数调用的时候,参数的值传递只是将实参a、b的值传递给p、q,并不是用a、b取代形参p、q进行操作(这就是我们理解的误区)。

2、地址传递:形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。

#include <iostream>
using namespace std;
void swap(int *p,int *q)
{
    int temp;
    temp=*p;
    *p=*q;
    *q=temp;
    //cout<<p<<""<<q<<endl;
}
int main()
{
    int a=1,b=3;
    swap(&a,&b);
    cout<<a<<endl;
    cout<<b<<endl;
     return 0;
}

最后输出的结果a=3,b=1;达到了a、b交换的效果。函数swap(int *p,int *q)中形参p、q均为指针,在函数引用的时候:swap(&a,&b);即相当于p=&a;q=&b;将a、b的地址赋值给了p、q,自然指针p指向了a,q指向了b,对*p,*q进行操作自然也就是对a、b的操作了,所以最后a、b的值进行了交换。这就是地址传递(将a、b的地址传递给形参)。

3、引用传递:形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作,在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

#include <iostream>
using namespace std;
void swap(int &p,int &q)//注意定义处的形式参数的格式与值传递不同
{
    int temp;
    temp=p;
    p=q;
    q=temp;
    //cout<<p<<""<<q<<endl;
}
int main()
{
    int a=1,b=3;
    swap(a,b);
    cout<<a<<endl;
    cout<<b<<endl;
     return 0;
}

最后输出的结果a=3,b=1;达到了a、b交换的效果。

  此时我们要注意与值传递的区别,函数定义的时候swap(int &p, int &q);其中标注了形参为引用,在调用函数swap(a,b)的时候进行参数传递,此时相当于&p=a;q=&b;即p为a的引用,q为b的引用,此时p、q就相当于a、b的别名,对p、q的操作也就是直接对a、b的操作。

  回到最上面的代码处,在调用函数GetMemory(str,100)的时候,str为指针,存储的为地址,进行参数传递的时候将str的地址传递给指针p,最开始指针p指向NULL,使用malloc开辟新的内存后,p就指向了新开辟的内存空间,但是实参str并没有改变,还是指向了NULL,p指向新开辟的内存空间后也没有释放,如此就造成了内存泄露,导致错误。

  对于修改方法有三种修改方法。

1、使用引用。

#include <iostream>
#include <malloc.h>
#include <string.h>
using namespace std;
void GetMemory(char *&p,int num)
{
    p=(char *)malloc(sizeof(char) * num);
}
void Test(void)
{
    char *str=NULL;
    GetMemory(str,100);
    strcpy(str,"hello");//
     cout<<str<<endl;
}
int main()
{
    Test();

    return 0;
}

输出结果为hello,符合预期期望。在函数定义时GetMemory(char* &p,int num),将指针p定义为引用,在参数传递的时候&p=str,即p为指针str的别名,对指针p的操作也就是对str的操作,所以指针str也会指向新开辟的内存空间。

2、使用函数返回值。

#include <iostream>
#include <malloc.h>
#include <string.h>
using namespace std;
char *GetMemory(char *p,int num)
{
    p=(char *)malloc(sizeof(char) * num);
    return p;
}
void Test(void)
{
    char *str=NULL;
    str=GetMemory(str,100);
    strcpy(str,"hello");
    //cout<<&str<<endl;
     cout<<str<<endl;
}
int main()
{
    Test();
    return 0;
}

输出结果为hello,符合预期期望。调用函数GetMemory(str,100);的时候返回的是指针p,再将str=GetMemory(str,100);进行赋值,相当于str=p,即str也指向了新开辟的内存单元,不再指向NULL,可以对其进行操作,最后输出我们所需要的结果。

3、使用二级指针即指向指针的指针。

#include <iostream>
#include <malloc.h>
#include <string.h>
using namespace std;
void GetMemory(char **p,int num)
{
    *p=(char *)malloc(sizeof(char) * num);

}
void Test(void)
{
    char *str=NULL;
    GetMemory(&str,100);
    strcpy(str,"hello");
    //cout<<&str<<endl;
     cout<<str<<endl;
}
int main()
{
    Test();
    return 0;
}

输出结果为hello,符合预期期望。函数定义中GetMemory(char **p,int num),p为二级指针,在函数调用GetMemory(&str,100)进行值传递的时候,此时相当于地址传递,指针*p=&str,*p存储的是指针str的地址,我们通过修改*p即是修改了str的地址,将*p指向新开辟的内存空间即是将str的地址指向了新开辟的内存空间,进而str也就指向了新开辟的内存空间,从而达到了动态内存传递的效果。

char *str=NULL;栈中操作的时候,内存为内存为0x28ff2c的地址存储内容为NULL

 

内存分配方式有三种:

  • 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
  • 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  • 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活。

 

posted @ 2017-08-21 01:43  小丁木  阅读(1003)  评论(0编辑  收藏  举报