• 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释放内存。动态内存的生存期由我们决定,使用非常灵活。
  • 相关阅读:
    [转]java常量池理解总结
    设计数据库时使用外键吗
    ajax 下载文件
    CentOS6.4配置163的yum源
    Xms Xmx PermSize MaxPermSize 区别
    [转]基于java的程序OutOfMemory问题的解决及Xms/Xmx/Xss的解释和应用
    Java Web Services面试
    java 单例模式
    Spring和MyBatis整合
    Spring核心概念
  • 原文地址:https://www.cnblogs.com/xiaodingmu/p/7402279.html
Copyright © 2020-2023  润新知