zoukankan      html  css  js  c++  java
  • c++用参数返回堆上的空间

    《高质量c++和c编程》7.4 指针参数是如何传递内存的一节中写道

    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"); // 运行错误 
    }

    无法返回内存,可以用如下方式

    void GetMemory2(char **p, int num) 
    { 
      *p = (char *)malloc(sizeof(char) * num); 
    }  
    void Test2(void) 
    { 
      char *str = NULL; 
     GetMemory2(&str, 100); //  注意参数是 &str ,而不是str 
     strcpy(str, "hello");  
      cout<< str << endl; 
     free(str);  
    }

    个人的理解就是,实际上指针传递仍然是一种值传递,只不过在参数是指针的时候,传递的是指针的副本,这样你在地址上的操作实际就反映到了内存中,举个例子来说,假设有一个函数

    void fun(int  *p)
    {
      p = new int;      
    }

    当用调用时fun(q),会产生实参的一个副本设为_p,函数体为副本_p分配了内存,实际上并未改变实参p,这就是GetMemory没有成功的原因。相反,如果我们有如下函数

    void fun(int *p)
    {
      *p = 3;  
    }

    在这个函数中,当发生实参调用的时候,仍然会产生实参的副本,但是注意这里不是改变副本,而是改变副本指向的内存中的内容,这里p是一个整形指针,在内存中占四个字节,副本和实参指向同一片内存,所以当你

    在以副本为地址的内存内赋值3,实际也就是改变了实参指向的内存中的内容。

    总结一下就是:指针传递仍然是值传递,所以我们在函数体内只有操作*p才会达到我们的指针传递要求,而不是操作p,这样操作只在副本上,实际并不反映到实参指向的内存。

    书中另一种方法是:

    char *GetMemory3(int num) 
    { 
      char *p = (char *)malloc(sizeof
     return p; 
    }  
    void Test3(void) 
    { 
      char *str = NULL; 
      str = GetMemory3(100);  
     strcpy(str, "hello"); 
      cout<< str << endl; 
     free(str);  
    } 

    实际是将堆的特性和return相结合,堆上分配的内存在函数不会释放,而return实际返回的p的一个副本,但是这里的副本是一个指针,简单的说是一个内存地址,而这个地址在函数结束后并没有释放,所以,我们可以继续

    使用。如果是普通的局部变量,return返回它的一个副本,随后局部变量随着函数的结束而被释放,这在某些时候会引起麻烦,比如

    char *GetString(void) 
    { 
      char p[] = "hello world"; 
     return p; // 编译器将提出警告 
    } 
    void Test4(void) 
    { 
    char *str = NULL; 
    str = GetString(); // str  的内容是垃圾 
    cout<< str << endl; 
    }

    至于return似乎还有东西说,一时想不起。。。

    事情总有例外,今天小妞找我调试程序,发现了一件很奇特的事情,看代码

    void getArray(char **s, int N)
    {
        std::ifstream in("test.txt");
        if (!in.is_open())
        {
            std::cout<<"error"<<std::endl;
        }
        int i = 0;
        while(i < N)
        {
            s[i] = (char*)malloc(sizeof(char)*100);
            in.getline(s[i], 100);
            ++i;
        }
        in.close();
    
    }
    int main()
    {
        char **s = (char **)malloc(sizeof(char) * 4);
        getArray(s, 4);
       for (int i=0; i<4; i++)
       {
           std::cout<<s[i]<<std::endl;
       }
        return 0;
    }

    这个程序可以正确编译执行。而下面代码

    void getArray1(char **s, int N)
    {
    //    s = (char **)malloc(sizeof(char) * 4);
        s = new char*[4];
        std::ifstream in("test.txt");
        if (!in.is_open())
        {
            std::cout<<"error"<<std::endl;
        }
        int i = 0;
        while(i < N)
        {
    //        s[i] = (char*)malloc(sizeof(char)*100);
            s[i] = new char[100];
            in.getline(s[i], 100);
            ++i;
        }
        in.close();
    
    }
    
    int main()
    {
        char **s;
        getArray1(s, 4);
        for (int i=0; i<4; i++)
        {
            std::cout<<s[i]<<std::endl;
        }
        return 0;
    }

    这个代码确实在运行时出错

    分析了一下,个人认为虽然两个函数的参数都是char **s,但是一个在main()中先分配,一个直接在getArray中分配,原因就在于此,getarray函数在main函数中先分配了内存,然后传递给它,虽然仍然是值传递,但是

    s的元素是指针,getarray函数中在main函数分配的内存上完成了操作,所以当函数结束时,所有操作仍然保留下来。getarray1函数不同,它是在函数体内完整分配内存,然后施加操作的,相当于都在副本上,所有操作都不会

    在函数结束后保留下来。

    这是个人的一点理解,如有不对的地方还请指教。

  • 相关阅读:
    一个例子讲明爬虫请求库requests
    SpringBoot【六】 Shiro
    SpringBoot【五】 Spring Security
    SpringBoot【四】 任务
    Swagger
    SpringBoot【三】 整合 JDBC、Druid、MyBatis
    SpringBoot【二】 SpringBoot 配置
    SpringBoot【一】入门程序及原理分析
    SpingBoot 相关问题:启动 web 项目之后出现 java.lang.UnsatisfiedLinkError: D:Tomcatapache-tomcat-9.0.36in cnative-1.dll
    SpringBoot 相关问题:IDEA 创建 SpringBoot 项目时出现 Initialization failed for 'https://start.spring.io' 问题
  • 原文地址:https://www.cnblogs.com/wghost/p/3215495.html
Copyright © 2011-2022 走看看