关于c++中一些指针问题的思考

 

昨天为了对c#的一些机制做更深的理解,特别用了c++的实现与之作对比。这不,c#没搞出大名堂,c++却揪出了一块烦心的事情:

还是昨天那个代码

 1

 

原本设计SendPersonByReference函数的功能就是传进一个对象的引用(指针和&引用都算,泛化的引用),改变该引用引用的对象。像上面那样设计好,自我感觉还挺良好,先造个辅存tmp,记录原对象的地址,接着析构delete,再将p指向新new的对象。一切ok(ps:运行的机子是dell的商用机,vista sp1英文版,vs2008 英文版) 运行结果如下图:

 

 2

pdpd的回住所,突发奇想,想到再运行下代码,于是在自己的小黑(win7 7000 英文版,vs2005)下balabala的敲好代码再运行,ft程序直接崩溃。。。。。。

刚开始想不通,很郁闷,但是后来仔细想想,却发现自己在c++ 指针上犯了一个特大的煞笔级别的错误:踩到了“栈上动态分配堆内存”的大地雷。

地雷p指针传入,用p new了一块堆内存,void的返回值。函数体一旦结束,拜拜,指针p被销毁了,那块可怜内存死在荒野无人问津,memory leak

内存问题先谈到这里,就单单谈功能的实现,其实我原先设计的函数是向下面这样的(更加煞笔)

void SendPersonByReference(Person *p)

{

                p->personAge = 555;     

                p = new Person("Nikki", 10000);

}

运行完出来结果:p指向的对象没变….首先,我总结为内存肯定是泄漏了,p在第二句掉头指向新new的对象。于是我就把代码改成了刚开始上面的代码,一运行,哈哈,在dell机上ok,以为万事大吉!谁知在回家后却发现代码重大的隐患

 

仔细分析:

1.       针对最开始的代码,首先就是出现了栈上动态分配堆内存的大忌(详见上面的地雷)

2.       从函数设计和功能上来说,我传入一个对象指针p,其实也只是对指针p的拷贝,函数体中实际在做的操作其实全都是p

 

3


 

p = new Person("Nikki", 10000);

这一句其实只是把p‘指向新得到的对象,而不是改变了真正需要改变的p。所以,就算你没有memory leak,它的运行的结果也不是你原先设计想要的答案。

3.       应该如何设计呢?

 void SendPersonByReference(Person **p)

{

 

    *p = new Person("Nikki", 10000);

    }

4

4.       最后一个疑问

为什么原先的设计在dell的机器上运行的时候,错误的设计却得到了正确的答案,而在别的机器上运行又是错误的呢?

这个问题我也没想特别的明白,windows以及编译器内部的处理机制也不是特别的清楚,估计应该是

void SendPersonByReference(Person *p)

{

          Person *tmp = p;

          p->personAge = 555;

          delete tmp;

         // p = new Person("fds",234);

  p = new Person("Nikki", 10000);

}

5

 

通过delete tmp我们将pp‘,tmp共同指向的内存区域“回收”了,pp’以及tmp成为了传说中的野指针。(事实上它们乱指一气吗?) 。通过p = new Person("Nikki", 10000);  p‘指向了新的内存区域,按照在dell上的运行结果反推回去,我们可以知道,新分配的内存其实就是原先被delete的内存区域,不然为何p(不是p)会指向新开辟的内存区域~。根据我的推断,也就是说编译器以及os将最新回收的内存立即又分配给了新的请求(new new Person("Nikki", 10000) ),具体vista以及编译器的内部实现,水平太差,有待继续深入研究。

 

为了进一步的验证我的臆测,我修改代码:

void SendPersonByReference(Person *p)

{

          Person *tmp = p;

          p->personAge = 555;

          delete tmp;

          p = new Person("damn",1234);

  p = new Person("Nikki", 10000);

}

运行结果和前面的运行结果一样,也就是说在函数栈没被destroy之前,p指向了“new Person("damn",1234);”的地址,p‘指向“p = new Person("Nikki", 10000);”指向的地址。当然了,函数体结束以后,遗漏了2块堆内存,一块(new Person("Nikki", 10000);)真的是泄露了,另一块(p = new Person("damn",1234);)歪打正着被原先的实参p指着。

 

总结:
依靠野指针未确定的行为进行编程是极其有害和邪恶的,我们当然应该避之不及。C++给了程序员足够的信任和自由去操作内存和系统,但也另外给了我们一颗颗隐患的地雷,继续修炼吧devx!以上有错误的地方的,还肯请大牛小牛们指点,俺现在水平就这么点,想到的全写了。止笔于此,该看c#去了…….

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章