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

在函数结束后保留下来。

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

c++用参数返回堆上的空间的更多相关文章

  1. C语言提高 (3) 第三天 二级指针的三种模型 栈上指针数组、栈上二维数组、堆上开辟空间

    1 作业讲解 指针间接操作的三个必要条件 两个变量 其中一个是指针 建立关联:用一个指针指向另一个地址 * 简述sizeof和strlen的区别 strlen求字符串长度,字符数组到’\0’就结束 s ...

  2. C++:在堆上创建对象,还是在栈上?

    这篇文章来自于一次讨论:http://www.devbean.net/2013/01/qt-study-road-2-model-view/#comment-17532.关于究竟是在堆上还是在栈上创建 ...

  3. 求你了,别再说Java对象都是在堆内存上分配空间的了!

    Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点,所以,即使是一个Java的初学者,也一定或多或少的对JVM有一些了解.可以说,关于JVM的相关知识,基本是每个Java开发者 ...

  4. 别再说Java对象都是在堆内存上分配空间的了!

    Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点,所以,即使是一个Java的初学者,也一定或多或少的对JVM有一些了解.可以说,关于JVM的相关知识,基本是每个Java开发者 ...

  5. JVM知识(一) 求你了,别再说Java对象都是在堆内存上分配空间的了!

    求你了,别再说Java对象都是在堆内存上分配空间的了! https://baijiahao.baidu.com/s?id=1661296872935371634&wfr=spider& ...

  6. Xamarin.Forms listview中的button按钮,实现带着参数返回上一级页面

    今天在做列表显示的时候遇到一个问题,就是在ListView中如何才能让一个button的按钮工作并且包含参数呢? 其实有点类似于rep里的控件无法起获取一样.在Xamarin中,当你button绑定事 ...

  7. 【Mybatis】MyBatis调用带有返回结果、output参数的存储过程上与ibatis的区别

    用过mybatis的应该都知道它是ibatis被Google收购后重新命名的一个工程,因此也做了大量升级.本文就来介绍下两者在调用存储过程上的一点区别,ibatis有一个专门的标签<proced ...

  8. python--函数的返回值、函数参数的使用、名称空间与作用域、函数嵌套、函数对象

    今天学习内容有函数的返回值.函数参数的使用.名称空间与作用域.函数嵌套. 下来我们一一查看. 函数的返回值 看几个栗子: def func(x): y=func() print(y) def foo( ...

  9. 只能在堆上生成的对象 VS. 只能在栈上生成的对象

    1. 只能在堆上 即禁止在栈上生成.如何实现? 当对象建立在栈上面时,是由编译器分配内存空间的,调用构造函数来构造栈对象.如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存. 所以,只需 ...

随机推荐

  1. dot net core 使用 usb

    原文:dot net core 使用 usb 本文告诉大家如何在 dot net core 使用 usb 目录 获得通知 读写 串口通信 LGPL 首先需要打开 Nuget 安装 CoreCompat ...

  2. dotnet core 使用 sqlite 部署到 Centos 服务器

    原文:dotnet core 使用 sqlite 部署到 Centos 服务器 本文告诉大家如何创建一个 asp dotnet core 程序,这个程序使用 sqlite 保存,部署程序到 Cento ...

  3. 共享Session

    概述 现在的大型网站中,会面临实现多台服务器中的session数据共享问题.当使用多台服务器架设成集群之后,我们通过负载均衡的方式,同一个用户(或者ip)访问时被分配到不同的服务器上,假设在A服务器登 ...

  4. PAT 1011-1020 题解

    早期部分代码用 Java 实现.由于 PAT 虽然支持各种语言,但只有 C/C++标程来限定时间,许多题目用 Java 读入数据就已经超时,后来转投 C/C++.浏览全部代码:请戳 本文谨代表个人思路 ...

  5. Chrome浏览器离线安装 Postman 5.X 报错

    下载和安装请参考 博友文章http://www.cnblogs.com/wangfeng520/p/5892125.html 尝试安装此扩展程序时出现以下警告: Ignored insecure CS ...

  6. 微信公众平台消息接口开发(2)你的服务器没有正确响应Token验证的解决方法

    你的服务器没有正确响应Token验证,请阅读消息接口使用指南 微信 微信公众平台开发模式 平台 消息 接口 启用 URL Token作者:http://txw1958.cnblogs.com/ 本系统 ...

  7. android4.0 USB Camera示例(四)CMOS

    上一页下一页说usb camera uvc标准 顺便说说CMOS一起做 操作基本一至, 前HAL在那里我已经提供了层CMOS相关接口 JNIEXPORT jint JNICALL Java_com_d ...

  8. matlab 二元函数的画法

    plot:画线(curve,二维空间以及三维空间) surf:画面(surface,一般在三维空间) 1. surf 绘图函数 surf 是 surface 的缩写,表示表面(显然至少三维图像才会有表 ...

  9. 一些自成系统、完备的教程(链接、博客、github等)

    0. Linus shell Advanced Bash-Scripting Guide 1. latex Some applicable LATEX's info 14 课的个人 CV 制作: 15 ...

  10. 大班模型行为PK(总结)

    行为类模式包括责任链模式.命令模式.解释器模式.迭代模式.中介模式.备忘录模式.观察者模式.State模式.策略模式.模板方法.Visitor模式,我去,许多.. .主要有以下挑几个easy混乱和控制 ...