c语言 指针 地址溢出,解密C语言中的指针和内存泄漏,这些陷阱要避开

访问空指针是非常危险的,因为它可能使您的程序崩溃。始终要确保您不是 在访问空指针。

没有躲过的坑--指针(内存泄露)

C++被人骂娘最多的就是指针。

夜深人静的时候,拿出几个使用指针容易出现的坑儿。可能我的语言描述有些让人费劲,尽量用代码说话。

通过指向类的NULL指针调用类的成员函数

试图用一个null指针调用类的成员函数,导致崩溃:#include using namespace std;class A{int value;public:void dumb() const{cout << 'dumb()\n';}void set(int x){cout << 'set()\n'; value=x;}int get() const{cout << 'get()\n'; return value;}};int main(){A *pA1 = new A;A *pA2 = NULL;pA1->dumb();pA1->set(10);pA1->get();pA2->dumb();pA2->set(20);//崩溃pA2->get();return 0;}

为什么会这样?

通过非法指针调用函数,就相当于给函数传递了一个指向函数的非法指针!

但是为什么pA2->dumb()会成功呢?

因为导致崩溃的是访问了成员变量!!

使用已经释放的指针struct X{int data;};int foo(){struct X *pX;pX = (struct X *) malloc(sizeof (struct X));pX->data = 10;free(pX);...return pX->data;}

使用未初始化的指针

如果你这样写,编译器会提示你使用了未初始化的变量p。void fooA(){int *p;*p = 100;}

那么如果我释放一个初始化的指针呢?void fooB(){int *p;free(p);}

结果是一样的!!

释放已经释放的指针

直接看看代码:void fooA(){char *p;p = (char *)malloc(100);cout << 'free(p)\n';free(p);cout << 'free(p)\n';free(p);}

这样的问题也许不会立即使你的程序崩溃,那样后果更加严重!!

没有调用子类的析构函数

之前的博客讲过,父类的析构函数最好声明为虚!!ParentClass *pObj = new ChildClass;...delete pObj;

上述代码会造成崩溃,如果父类的析构函数不声明为虚,那么不会调用继承类的析构函数,造成内存泄露。

内存溢出

当我们拷贝字符串的时候,我们常常会用到 memcpy函数。这里特别需要注意的就是字符串结尾的null字符:char *p = (char *)malloc(strlen(str));strcpy(p, str);

为了躲过这个坑,只需要把 strlen(str) 改为 strlen(str)+1。

记一次指针使用不当造成的内存泄露

刚写完一段代码,由于将很运行在移动设备上, 我决定先测试一下内存的使用量, 结果发现了很严重的内存泄漏, 在前前后后翻看了new 和delete并确认没有漏写的情况下, 泄露依然存在!调试后最终确认了问题是因为union的不当使用造成的, 下面开始还原现场:typedef struct DATA_{DATA_(int size = 10){pVoid = new char[nSize];this->size = size;}virtual ~DATA_(){if (pVoid){delete pVoid;}}int nSize;void *pVoid;}DATA, *LPDATA;struct STRUCT1{STRUCT1(int count){pVoid = new DATA[count];this->count = count;}virtual ~STRUCT1(){if (pVoid){delete pVoid;}}int count;union{void *pVoid;LPDATA pData;}}int main(int argc, char* argv[]){for( int i = 0; i < 1000; i++){void *p = new STRUCT1( 10 );delete p;}return 0;}

在上面这段代码中,STRUCT1中包含了若干个DATA结构,DATA结构又申请了默认为10byte大小的内存,并且内存在对象析构的时候会用delete回收。乍一看这个代码貌似不会泄露内存,其实不然,待我分析:

在C++中构造函数与析构函数的调用是由编译器完成的,其中构造函数的调用一般是在对象空间开辟完以后(栈对象或者堆对象都是一样的),将参数压栈,this指针(对象的起始地址)放入eax寄存器(不同的编译器做法可能不同),然后跳到构造函数去执行。而析构函数的调用则是当对象内存被回收的时候被调用(栈对象是当代码执行到变量可见域外之前调用, 堆对象是在delete语句的位置进行调用)。

然而编译器并是那么智能的可以理解coder的意图,析构函数的调用是根据当前delete的指针类型来确定的,而(下面)这段代码却没有提供类型, 这导致了DATA_的析构函数将不会被调用,内存泄漏就在所难免了。virtual ~STRUCT1(){if (pVoid){delete pVoid;//问题在这, 应该使用delete pData;}}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值