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, ); // 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++用参数返回堆上的空间的更多相关文章
- C语言提高 (3) 第三天 二级指针的三种模型 栈上指针数组、栈上二维数组、堆上开辟空间
1 作业讲解 指针间接操作的三个必要条件 两个变量 其中一个是指针 建立关联:用一个指针指向另一个地址 * 简述sizeof和strlen的区别 strlen求字符串长度,字符数组到’\0’就结束 s ...
- C++:在堆上创建对象,还是在栈上?
这篇文章来自于一次讨论:http://www.devbean.net/2013/01/qt-study-road-2-model-view/#comment-17532.关于究竟是在堆上还是在栈上创建 ...
- 求你了,别再说Java对象都是在堆内存上分配空间的了!
Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点,所以,即使是一个Java的初学者,也一定或多或少的对JVM有一些了解.可以说,关于JVM的相关知识,基本是每个Java开发者 ...
- 别再说Java对象都是在堆内存上分配空间的了!
Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点,所以,即使是一个Java的初学者,也一定或多或少的对JVM有一些了解.可以说,关于JVM的相关知识,基本是每个Java开发者 ...
- JVM知识(一) 求你了,别再说Java对象都是在堆内存上分配空间的了!
求你了,别再说Java对象都是在堆内存上分配空间的了! https://baijiahao.baidu.com/s?id=1661296872935371634&wfr=spider& ...
- Xamarin.Forms listview中的button按钮,实现带着参数返回上一级页面
今天在做列表显示的时候遇到一个问题,就是在ListView中如何才能让一个button的按钮工作并且包含参数呢? 其实有点类似于rep里的控件无法起获取一样.在Xamarin中,当你button绑定事 ...
- 【Mybatis】MyBatis调用带有返回结果、output参数的存储过程上与ibatis的区别
用过mybatis的应该都知道它是ibatis被Google收购后重新命名的一个工程,因此也做了大量升级.本文就来介绍下两者在调用存储过程上的一点区别,ibatis有一个专门的标签<proced ...
- python--函数的返回值、函数参数的使用、名称空间与作用域、函数嵌套、函数对象
今天学习内容有函数的返回值.函数参数的使用.名称空间与作用域.函数嵌套. 下来我们一一查看. 函数的返回值 看几个栗子: def func(x): y=func() print(y) def foo( ...
- 只能在堆上生成的对象 VS. 只能在栈上生成的对象
1. 只能在堆上 即禁止在栈上生成.如何实现? 当对象建立在栈上面时,是由编译器分配内存空间的,调用构造函数来构造栈对象.如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存. 所以,只需 ...
随机推荐
- PDW中的Split Querying Process
最近看了关于 SQL Server 的分布式处理方面的论文,觉得它提出的 Polybase 跟之前看过的 HadoopDB 有些神似,这里做个小总结(抽空再把 HadoopDB 的总结贴出来). 不算 ...
- WPF依赖属性(续)(1)
原文:WPF依赖属性(续)(1) 之前有写过几篇文章,详细地介绍了依赖属性的基本使用方法,如果你不想了解其内部实现机制的话,那么通过那两篇文章的介绍,足以应付平时的应用 ...
- 6 Wcf使用Stream传输
1.创建service和client项目 service项目新建wcf服务文件 MediaService 和 IMediaService IMediaService 代码为 using System. ...
- Maven环境下Poi的使用
Poi的使用方法,网上还是挺多的,官网也有教程. 附一个比较全的: http://www.cnblogs.com/vTree/archive/2011/11/30/2268721.html 需要说明的 ...
- 数值范围选择控件RangeSlider
原文:数值范围选择控件RangeSlider RangeSlider控件用于在一个指定上下限的范围中选择一个数值范围,因此该控件的Maximum和Minimum属性用于指定上下限:而Selection ...
- 将枚举转成SelectListItem
代码如下: /// <summary> /// 将一个枚举转化成一个List<SelectListItem> /// </summary> /// <type ...
- ADO.net Connection对象简介
Connection对象 学习的是刘皓的文章 ADO.NET入门教程(四) 品味Connection对象 这篇文章开始水平一般起来了,主要介绍了要优雅的使用这个对象 1 用try...catch.. ...
- matlab 工具函数 —— logdet(A)
当参数 A 是正定矩阵(positive definite)时,logdet 利用相关矩阵分解的性质,将比 log(det(A)) 获得更快的效率: function y = logdet(A) tr ...
- OpenGL(二十二) gluBuild2DMipmaps 加载Mip纹理贴图
当纹理被用于渲染一个面积比它本身小很多的对象时,会由于纹理图像的降采样率不足而导致混叠现象,主要的表现特征是纹理图像的闪烁,出现纹理躁动.特别是在场景远近移动变换时,这种闪烁情况更为明显,严重可能会影 ...
- 极简代码(八)—— binary activation function
二值化的激活函数: x > 1 ? 1 : -1; ⇒ [1, -1]; x = 0 ⇒ -1; 当然也可以使用sign() 函数(求符号函数): sign(x) % 但要注意的是,sign(0 ...