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函数不同,它是在函数体内完整分配内存,然后施加操作的,相当于都在副本上,所有操作都不会

在函数结束后保留下来。

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

posted @ 2013-07-25 22:09  没出没  阅读(1505)  评论(7编辑  收藏  举报