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函数不同,它是在函数体内完整分配内存,然后施加操作的,相当于都在副本上,所有操作都不会
在函数结束后保留下来。
这是个人的一点理解,如有不对的地方还请指教。