关于返回局部变量(栈内存)的指针或引用

 

关于返回局部变量(栈内存)的指针或引用

 

年轻的人们,你们要相信科学手段,对于科学的奥秘,我们几乎一无所知。不要气馁,你们要生活在肃然宁静的实验室和图书馆中。这样到你生命的尽头,你将可以说,我已经尽力而为了。--- 巴斯德(Louis Paster,1822-1895,法国人,著名化学家)

 

这是一个网友提的一个问题,源代码如下:(我在VC6环境下作了一个分析)

 

// Test.cpp

#include 
< iostream >

using   namespace  std; 

int  a  =   5 ;

int   * example1( int  b)

    a 
+= b;
    
return &a;
}


int   * example2( int  b)
{
    
int c = 5;
    b 
+= c;
    
return &b;
}


int  main()
{
    
int *a1 = example1(2);
    
int *b1 = example2(4);
    
    cout 
<< " a1 = " << *a1 << endl;
    cout 
<< " b1 = " << *b1 << endl;
 
    
return 0;
}

 

 

(1) 结果:

 a1 = 7
 b1 = 4198610
 (注:4198610对应的十六进制为0x4010D2)
 

 

(2) 实验过程:

(以下说的Watch窗口,Disassembly窗口,Memory窗口等都是指View ->Debug Windows下的几个窗口,^_^,很有用的.)

我在两个cout语句前均设置了断点,在watch窗口Name下设置*b1,发现执行到第一个cout语句时,*b1仍为9;但是执行完第一个cout语句时,*b1就变为4198610.

另外,不通过在Watch窗口中查看*b1的Value,同样可以看到结果,方法是通过Disassembly窗口得到的汇编代码,
可以计算出b变量所在的有效地址:
16:       return &b;
004015E8   lea         eax,[ebp+8]
通过查看寄存器ebp中的值,再加上8即为b变量的有效地址,同时作为返回值赋给b1.这里为0x12FF28.
但是可以通过这个有效地址在Memory窗口观察*b1对应的值.
当执行到第二个cout语句时,0x12FF28的值变为D2 10 40,即0x4010D2,对应十进制4198610.


(3) 原因分析:

这是因为在退出某函数后,函数栈帧释放以后,其空间就可以被程序本身的其它部分或者其它程序占用.
因而其结果是不确定的.(例如在GCC下运行这个程序就会得到不一样的结果.)这一点在<< Effective C++ 2nd >> Item31上也讲到了.

另外一个小小的技巧,就是Memory窗口输入的虽然是变量的有效地址,但是结果还是能找到对应的变量的值.


(4) 作一点重要的补充:

 

// Test2.cpp
#include  < cstdio >

char *  get_str()
{
    
char   * str  =  { " abcd " }; // 可以
    
// char str[] = {"abcd"}; // 错误,结果不确定
     return  str;
}

int  main()
{
    
char *  p  =  get_str();
    printf(
" %s " , p);
    
    
return   0 ;
}

char *str = {"abcd"};//可以,是因为"abcd"是一个字符串常量,它并不在一个栈空间上,而是在静态存储区上。
字符串的生命周期是:程序开始时分配,程序结束时释放。
(当然,str是一个指针变量,在栈空间上,因此函数返回时会释放,但是所指为字符串常量,字符串常量所占空间在函数返回时不会释放)

而char str[] = {"abcd"};//错误,结果不确定,虽然从编译器的实现来看(见如下代码(VC6下)的结果),"abcd"仍然是当成一个字符串常量,但是程序又把它复制了一次,放在了栈空间上,返回的时候str所指的是栈上的这一块字符串。问题的本质仍然是由于返回了指向栈空间的指针。
 

// 5:        char *str = {"abcd"};
// 00401038   mov         dword ptr [ebp-4],offset string "abcd" (0042201c)
// 6:        return str;
// 0040103F   mov         eax,dword ptr [ebp-4]

// 5:        char str[] = {"abcd"};
// 00401038   mov         eax,[string "abcd" (0042201c)]
// 0040103D   mov         dword ptr [ebp-8],eax
// 00401040   mov         cl,byte ptr [string "abcd"+4 (00422020)]
// 00401046   mov         byte ptr [ebp-4],cl
// 6:        return str;
// 00401049   lea         eax,[ebp-8]

 


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值