这个函数这样写会不会造成不可预知的结果?

cutenoob 2007-03-17 01:37:24
struct Node
{
int i;
string str;
}; //创建一个节点

Node GetSomething()
{
Node temp;
temp.i = 0;
temp.str = "abc";

return temp;
} //返回一个节点

void DoSomething()
{
Node First = GetSomething();
}

----

我觉得GetSomething()不正确,在调用的时候有可能会出错.因为返回的是栈上开辟的内容.

可我朋友说这样写没问题.而且说这是返回的值,跟栈没关系.First能得到值.

当然我也测试过,结果他是对的,可是我觉得返回栈上的内容还是会引发些问题.
...全文
288 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
pass86 2007-03-17
  • 打赏
  • 举报
回复
好文众多的好回复,
对于楼主的问题我同意沙发的。
iamcaicainiao 2007-03-17
  • 打赏
  • 举报
回复
http://faq.csdn.net/read/207466.html
供参考




======================================
堆和栈 的区别在哪里?

哪些变量会在栈中分配空间,哪些变量会在堆中分配空间?
---------------------------------------------------------------

1.申请方式
stack:
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在栈中的。


2 申请后系统的响应

栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

3.申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。


4.申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。

5.堆和栈中的存储内容
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

6.存取效率的比较

char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

---------------------------------------------------------------

局部变量,函数形参是存储在栈上的,由系统帮你管理
程序员通过new分配的内存,是在堆上的,要由自己用delete来释放
---------------------------------------------------------------

動態分配的內存在堆里。
---------------------------------------------------------------


栈是系统自动为之分配的,例如当在调用涵数时,需要保存的变量,最明显的是在递归调用时,要系统自动分配一个栈的空间,后进先出的,而后又由系统释放这个空间,
堆是自已申请自已释放,如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = (char *)malloc(10);
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
---------------------------------------------------------------

不是很清楚区别啊!
---------------------------------------------------------------

栈可以扩大
vc 编译选项中可以设置,其实就是一个 /STACK参数

缺省2M
---------------------------------------------------------------

栈空间的分配通常要受到操作系统的限制,因为栈的分配是从高地址到底地址;而堆空间的分配是从底地址到高地址,所以一般不会受到限制。虽然栈的空间有限,但是其访问速度比堆快,而堆的使用更为灵活...
---------------------------------------------------------------

堆和栈其实是两种数据结构……

只不过系统的进程空间管理引用了这两种结构,所以空间被分成了堆和栈……
---------------------------------------------------------------

堆和栈 的区别在哪里?
======================================================================================
堆和栈是编译器划分的内存空间。

栈上分配的内存,编译器会自动收回;堆上分配的内存,要通过free来显式地收回。


哪些变量会在栈中分配空间,哪些变量会在堆中分配空间?
=======================================================
函数局部变量、参数,一些临时对象都在栈中分配空间。

用malloc、calloc等申请的内存在堆中分配。

iamcaicainiao 2007-03-17
  • 打赏
  • 举报
回复
char *GetMemory3(int num)
{
char *p = (char *)malloc(sizeof(char) * num);
return p;
}
-------------------------
这个就是可以的.
只是,
用完了之后,不要忘记释放曾经申请的空间.
iamcaicainiao 2007-03-17
  • 打赏
  • 举报
回复
而指针不可以return出去..
-------------------------------------
还包括引用.
----------------------------
但是,
并非

指针和引用就不可以return出去.
是栈上的内存不可以return出去.

堆上的还是可以的.
foring 2007-03-17
  • 打赏
  • 举报
回复
iamcaicainiao解释得很好了,其原因的根本在于编译器产生的临时变量,对于这样的问题,如果存在浅拷贝的隐患,那么就会出问题了
cutenoob 2007-03-17
  • 打赏
  • 举报
回复
呵呵,,书看的少啊...

简单来说可不可以这样:

对象是可以retrun出去的,而指针不可以return出去..
iamcaicainiao 2007-03-17
  • 打赏
  • 举报
回复
下面摘自<高质量c/c++编程指南>

=========================================================
 【规则6-3-2】在函数体的“出口处”,对return语句的正确性和效率进行检查。
如果函数有返回值,那么函数的“出口处”是return语句。我们不要轻视return语句。如果return语句写得不好,函数要么出错,要么效率低下。
注意事项如下:
(1)return语句不可返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。例如
char * Func(void)
{
char str[] = “hello world”; // str的内存位于栈上

return str; // 将导致错误
}
(2)要搞清楚返回的究竟是“值”、“指针”还是“引用”。
(3)如果函数返回值是一个对象,要考虑return语句的效率。例如
return String(s1 + s2);
这是临时对象的语法,表示“创建一个临时对象并返回它”。不要以为它与“先创建一个局部对象temp并返回它的结果”是等价的,如
String temp(s1 + s2);
return temp;
实质不然,上述代码将发生三件事。首先,temp对象被创建,同时完成初始化;然后拷贝构造函数把temp拷贝到保存返回值的外部存储单元中;最后,temp在函数结束时被销毁(调用析构函数)。然而“创建一个临时对象并返回它”的过程是不同的,编译器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的化费,提高了效率。
类似地,我们不要将
return int(x + y); // 创建一个临时变量并返回它
写成
int temp = x + y;
return temp;
由于内部数据类型如int,float,double的变量不存在构造函数与析构函数,虽然该“临时变量的语法”不会提高多少效率,但是程序更加简洁易读。
iamcaicainiao 2007-03-17
  • 打赏
  • 举报
回复
#include<iostream>
#include <string.h>
using namespace std;

string getstr()
{
string str;
str = "abc";
return str;
}

char* getstr1()
{
char str[] = "abc";

return str;
}


int main()
{
string a;
char * b;

a = getstr();
b = getstr1();

cout<<a<<endl;//正确输出abc
cout<<b<<endl;//输出乱码,有时也可能正确输出.

system("PAUSE");
return EXIT_SUCCESS;
}
iamcaicainiao 2007-03-17
  • 打赏
  • 举报
回复
string getstr()
{
string str;

str = "abc";
return str;
}

char* getstr1()
{
char str[] = "abc";

return str;
}
================================================
前一个没有问题.参数返回值.返回string类型.跟返回int,等一样处理.
具体说,就是,会生成临时变量.

后一个有问题.返回指针.
离开函数后,栈已经清除.
返回指向栈的指针.自然不合理了.
biblereader 2007-03-17
  • 打赏
  • 举报
回复
不会造成不可YU知的结果, 但是, 这样的写的代码, 如果你的NODE是一个很大的数据结构, 效率方面会比较差.
wuming2003 2007-03-17
  • 打赏
  • 举报
回复
对于这一个的确是比较复杂


Node GetSomething()
{
Node temp;
temp.i = 0;
temp.str = "abc";

return temp;
}

转化为c代码可能是这样的
void GetSomething(Node *result)
{
Node temp;
temp.i = 0;
temp.str = "abc";

result->i = temp.i;
restul->str = temp.str;
return;
}

如果你这样调用函数Note p = GetSomething(); 转化为C代码可能是
Note p;
GetSomething(&p);

大概就是这样,可能不是特别准确,详情请参考<<深度探索C++对象模型>>
cutenoob 2007-03-17
  • 打赏
  • 举报
回复
我是这样想的..

如果return temp ,因为temp是在函数内声明的,所以在函数运行结束后temp的生命周期就到了,,要消灭自己..然后赋值 First ,可能这时候temp已经不存在了,拿什么传给他?
web518 2007-03-17
  • 打赏
  • 举报
回复
不要被结构体迷惑。如果我没理解错的话,你可以看看下边的例子。
string getstr()
{
string str;

str = "abc";
return str;
}

char* getstr1()
{
char str[] = "abc";

return str;
}

第一个没问题。第二个就会出错。道理是一样的!

另你的结构体声明没有加typedef。后边可以直接用node声明吗?
abcdefgh111807 2007-03-17
  • 打赏
  • 举报
回复
你觉得会引发什么问题??
不太理解!!
cutenoob 2007-03-17
  • 打赏
  • 举报
回复
怪了...这个和

Node GetSomething()
{
Node temp;
temp.i = 0;
temp.str = "abc";

return temp;
}

char *Dosomething()
{
char str[] = "abc"
return str;
}
这两个不是一样的意思么?都是在栈上声明了空间,在函数结束后自动析构str...

wuming2003 2007-03-17
  • 打赏
  • 举报
回复
你那样写没有问题

这样才有问题
Node* GetSomething()
{
Node temp;
temp.i = 0;
temp.str = "abc";

return &temp;
} //返回一个节点

void DoSomething()
{
Node* First = GetSomething();
}

64,648

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧