《高质量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. Windows Container 和 Docker

    Windows Container 和 Docker 微软在2016年的Ignite技术大会上正式发布了Windows Server 2016,其中的容器服务已经可以作为生产环境使用.这意味着Wind ...

  2. shell问题集合

    1.syntax error near unexpected token `then' if后要有空格,[] 中括号的开头和结尾要有空格! [ $1-eq"root" ]中括号中的 ...

  3. 北大SQL数据库视频课程笔记

    Jim Gray - Transaction processing: concepts and techniqueshttp://research.microsoft.com/~gray/ 事务概念 ...

  4. 给WPF示例图形加上方便查看大小的格子

    原文:给WPF示例图形加上方便查看大小的格子 有时,我们为了方便查看WPF图形的样式及比例等,需要一些辅助性的格线,置于图形.图像的背景中. 比如下图,就是为了更清晰地查看折线的图形,我们画了用于标示 ...

  5. HDU 1618 Oulipo KMP解决问题的方法

    鉴于两个字符串,寻找一个字符串的频率,另一个字符串出现. 原版的kmp另一个陷阱.以下凝视了,标不是踩着好,有加班一定几率,也有机会错误,根据不同的字符串可以是详细. 变化看起来像一个,kmp速度是非 ...

  6. Oracle 一些实用的DBA语句

    --查询LOB的大小和所在表空间 SELECT A.TABLE_NAME, A.COLUMN_NAME, B.SEGMENT_NAME, B.SEGMENT_TYPE, B.TABLESPACE_NA ...

  7. matlab 各种文件的读取(及读写问题的解决)

    0. 文本文件 load('**.mat') load('**.mat', '-ascii') load('-mat', filename) load('-ascii', filename) 1. 音 ...

  8. pandas 学习(二)—— pandas 下的常用函数

    import pandas as pd; 1. 数据处理函数 pd.isnull()/pd.notnull():用于检测缺失数据: 2. 辅助函数 pd.to_datetime() 3. Series ...

  9. [WPF] PerformClick ?

    原文:[WPF] PerformClick ? [WPF] PerformClick ?  周银辉 WPF没有提供这个方法,还真是让人觉得有些讨厌啊.而关于这个嘛,Google中搜一下,一大堆,但一般 ...

  10. JavaScript:undefined And null差异

    班吃饭的时候,同事偶然问了一个问题:undefined和null究竟有什么差别?无法回答,回去查阅相关文档,算了有了一个了解,做相关的总结.在開始之前,请看例如以下代码,算是抛出这个问题: conso ...