C++ 内存管理的小问题--------危险的C++

来源:互联网 发布:cgi php asp 编辑:程序博客网 时间:2024/04/28 13:09

C++ 内存管理的小问题--------危险的C++


          今天看<<C++ Primer>>十三章的智能指针部分,了解到通过引用计数可以解决悬垂指针问题,这个方案解决的是多个指针指向同一对象时何时真正释放空间的问题。但是貌似还有其他悬垂的情况。那就是指向的的是栈空间的情况。


情况一:

#include<iostream>using std::cout;using std::endl;int main(){int *p;cout<<"&p = 0x"<<&p<<endl;{int data = 10;cout<<"&data = 0x"<<&data<<endl;p = &data;cout<<*p<<endl;}int fill = 50;cout<<*p<<endl;*p = 1000;{int  f = 80;}cout<<*p<<endl;   return  0;}

该段程序让我非常困惑,局部变量data应该在出局部作用域后就被释放掉了。为什么*p仍然可以正常读写呢?其实如果还记得我们在<<C和指针>>中对指针的讨论的话,那么这不难解释:局部变量的释放(栈空间的释放)其实并非回收程序对其的读写权限,而只是标记该段空间可以被重新分配。即使重新分配仍然不会回收指针对其的访问(读写)权限。之所以作用域之后data无法访问该段内存这并不是说回收了权限,而只是在外部作用域无法使用名字data的缘故罢了,这是编译器行为。再次重申在效率优先的原则下:C/C++回收空间时是不会回收指针已有的访问权限,也不会清理内存空间,仍然保持释放前的数据,它所要做的仅仅是标记这段空间可以被重新分配罢了。当重新分配之后,原指针和重新分配后标记该段内存的变量或指针都可以对其读写,但这往往是错误的根源,明智的选择是释放空间之后把指向它的所有指针置位NULL


错误情景一:

当我们释放空间然后重新分配空间后,当不小心在程序运行过程中使用了悬垂指针,会对程序数据进行破坏,影响程序正常运行。

 

错误情景二:

见代码:

#include<stdio.h>#include<stdlib.h>struct dataSet{int *ptr;};int main(){int a = 8;struct dataSet *pa =(struct dataSet*)malloc(sizeof(struct dataSet));free(pa);(1)pa->ptr = &a;  //使用这句会运行出错    (2)pa->ptr = (int*)malloc(sizeof(int));//运行正常printf("pa->ptr = %d\n",*(pa->ptr));printf("&pa = 0x%x\n",&pa);printf("pa = 0x%x\n",pa);int *q = (int*)malloc(sizeof(int));*q = 100;printf("&q = 0x%x\n",&q);printf("q = 0x%x\n",q);(3)free(q);return 0;}

本例中若使用(1)则运行出错,若使用(2)则运行成功,出错点在(3),为什么?暂时我也没想通。

答:根据CSDN解答,(1)(2)有可能出错也可能不出错,C/C++没有定义,跟具体的操作系统和编译器实现有关。

 

至此我们得出一个忠告:

(1)       指针使用之前必须保证初始化;

(2)       释放内存后指针置NULL;

(3)       千万不要企图使用free,delete掉的指针,虽然理论上允许,但这绝对是会使系统变得更加糟糕。

(4)       当把指针指向局部变量时,我们最好使该指针的作用域与该局部变量相同,否则我们需要显示的在超出局部作用域之后将指针置NULL,以免出错。

情况二:

 

#include<iostream>#include<stdlib.h>using std::cout;using std::endl;class A{public:A(){}void show(){cout<<"*ptr = "<<*ptr<<endl;}void set(int *p){ptr = p;}int *GetPtr(){return ptr;}private:int *ptr;};void Set(A& arg){int data = 100;arg.set(&data);}void test(){int data = 1000;int data1 = 1000;int data2 = 1000;}int main(){A a;Set(a);(1)test();a.show();        *(a.GetPtr()) = 999;a.show();}

不使用语句(1)输出为100,使用则输出1000,这充分证明了情况一中的解释是对的,调用test会覆盖掉函数调用栈中的100。但是指针仍然具有读写权限。这样程序无法保持数据读写的一致性,会出现读取垃圾值的情况。

 

        无论指针指向静态空间(一般为栈),还是动态空间(一般为堆),释放空间时处理的决策相同,对指针的权限并不撤销,只是标记该段空间可被再次分配。唯一不同在于静态空间释放由编译器控制,而动态空间由程序员控制。

 

        另外malloc/free  和new/delete要搭配使用,不要malloc然后delete,这大多情况是会出错的(我猜)。

 

下一篇将讨论malloc分配内存的一些细节,以及free的细节。