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

void GetMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str = NULL;
GetMemory(str, ); // 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, ); // 注意参数是 &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 = ;
}

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

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

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

书中另一种方法是:

char *GetMemory3(int num)
{
char *p = (char *)malloc(sizeof
return p;
}
void Test3(void)
{
char *str = NULL;
str = GetMemory3();
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 = ;
while(i < N)
{
s[i] = (char*)malloc(sizeof(char)*);
in.getline(s[i], );
++i;
}
in.close(); }
int main()
{
char **s = (char **)malloc(sizeof(char) * );
getArray(s, );
for (int i=; i<; i++)
{
std::cout<<s[i]<<std::endl;
}
return ;
}

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

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

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

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

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

在函数结束后保留下来。

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

最新文章

  1. java入门 第一季3
  2. ZFS(一):ZFS在Debian GNU/Linux上的安装
  3. hdu 5691 Sitting in Line 状压dp
  4. vs2008编译boost
  5. js 函数的调用模式
  6. bashrc的加载
  7. C#.Net 如何动态加载与卸载程序集(.dll或者.exe)3---- 动态加载Assembly应用程序
  8. careercup-C和C++ 13.3
  9. select unit_timestamp(); 和select unit_timestamp(&quot;1970-1-1 08:00:00&quot;)和 select from_unixtime(1)
  10. STM8S学习笔记-时钟控制2
  11. Unix/Linux环境C编程入门教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建
  12. ExtJS001HelloWorld弹窗
  13. 【原创】构建高性能ASP.NET站点 第七章 如何解决内存的问题(前中篇)—托管资源优化—监测CLR性能
  14. 配置Linux Kernel时make menuconfig执行流程分析
  15. javascript集合的交,并,补,子集,长度,新增,删除,清空等操作
  16. 脚本检测Kafka和Zookeeper
  17. [朴孝敏][Ooh La La]
  18. 前端基础-html 介绍和head标签 ( 1 )
  19. bzoj 4912: [Sdoi2017]天才黑客
  20. Chapter One:数据结构和算法-解压序列赋值给多个变量

热门文章

  1. 使用nodejs 来压缩整个目录
  2. AbsoluteLayout绝对布局
  3. VC命令行编译中出现Invalid switch错误的解决办法
  4. Vue 兄弟组件之间传递数值
  5. 【转】mac版微信web开发者工具(小程序开发工具)无法显示二维码 解决方案
  6. Java 中override、overload、overwrite区别,以及与多态的关系【转】
  7. WPF DataGrid自动生成列
  8. .Net中使用数据库(sqlite)的大体流程(简单向)
  9. SyncML是一平台无关的信息同步标准协议集
  10. php将两个数组相同的key合并到一个数组