C/C++中的内存泄漏、内存溢出、野指针

内存泄漏

  • 概念:内存泄漏是指我们在内存中申请(new/malloc)了一块内存,但是没有去动手释放(delete/free)内存,导致指针已经消失,而指针所指向的空间还被占用,系统就已经不能控制这块空间了。使用完变量之后却没有及时回收这部分内存,这时我们说发生了内存泄漏。如果发生了内存泄漏有没有及时发现,随着程序的运行,程序占用空间会越来越大,直到消耗完系统的所有内存,系统就会崩溃。
发生原因
  • 程序循环new创建出的对象没有及时delete掉,导致内存泄漏。
  • delete掉一个void* 类型的指针,导致没有调用到对象的析构函数,析构的所有清理工作都没有去执行从而导致内存的泄漏。
class Object {
private:
    void* data;
    const int size;
    const char id;
public:
    Object(int sz, char c):size(sz), id(c){
	    data = new char[size];
	    cout << "Object() " << id << " size = " << size << endl;
    }
    ~Object(){
	    cout << "~Object() " << id << endl;
	    delete []data;
    }
};
int main() {
	Object* a = new Object(10, 'A');//Object*指针指向一个Object对象;
	void* b = new Object(20, 'B');//void*指针指向一个Object对象;
	delete a;//执行delete,编译器自动调用析构函数;
	delete b;//执行delete,编译器不会调用析构函数,导致data占用内存没有得到回收;
	
	cout << "Press any key to continue... ..." << endl;
	getchar();
	return 0;
}

  • new创建一组对象数组,内存回收的时候缺只调用了delete而非delete[]来处理,导致只有对象数组的第一个对象的析构函数得到执行并回收了内存,而其他对象所占内存得不到回收。
class Object1
{
    int a;
    int b;
};
int main() {
	Object1* arry1 = new Object1[100];//创建包含100个Object1的对象数组arry1并返回数组首地址;
	Object1* arry2 = new Object1[100];//创建包含100个Object1的对象数组arry2并返回数组首地址;
	delete []arry1;//回收了数组arry1里的所有对象动态创建时占用的内存空间;
	delete  arry2;//回收了数组arry2里的第一个对象动态创建时占用的内存空间,导致其他99个对象的内存空间泄露;
	
	cout << "Press any key to continue... ..." << endl;
	getchar();
	return 0;
}

内存溢出

  • 概念:内存溢出out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请一个int,但是给其存了long才能存得下的数,那就是内存溢出。
发生原因
  • 内存中加载的数据量过于庞大;
  • 代码中存在死循环;
  • 递归调用太深,导致栈溢出;
  • 内存泄漏最终导致内存溢出。

为什么说strcpy和strncpy是C++的危险函数

  • 1.strcpy
    • 函数原型为char *strcpy(char *dest,const char *src);
    • 函数说明:strcpy函数会将参数src字符串拷贝至参数dest所指的地址。
    • 参数说明:dest,我们说的出参,最终得到的字符串。src,入参,因为其有const修饰。表示在此函数中不会也不能修改src的值。
    • 返回值:返回dest字符串的起始地址。
    • 附加说明:如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况。次函数很好用,可是它也很危险,如果在使用之前加上相关的长度判断,则会大大降低出错的危险。此函数还有一个特点,就是它在把字符串b拷贝到字符串a的时候,会在拷贝的a字符串的末尾加上一个\0结束标志。这一点不同于strncpy()函数。
  • 2.strncpy
    • 函数原型为:char *strncpy(char *dest,const char *src ,size_t n);
    • 函数说明:strncpy会将参数src字符串拷贝前n个字符至参数dest所指的地址。
    • 返回值:返回参数dest的字符串起始地址。
    • strncpy的正确用法如下:(size一定要用sizeof(dest)或sizeof(dest)-1,不能误用为sizeof(src)。手工补0,务必将dest的最后一个字节手动置为\0。因为strncpy只在src的长度小于dest时,对剩余的字节填0,而当dest长度远大于src长度时,对多于的自己填\0,会造成很大的性能损失。
strncpy(dest, src, sizeof(dest));
dest[sizeof(dest)-1] = ‘\0;

野指针

  • 指向已经删除的对象或者申请访问受限内存区域的指针,称为野指针。它与空指针不同,野指针无法通过简单地判断是否为NULL避免,只能通过良好的编程习惯来避免。对野指针进行操作很容易造成程序错误。
    • 指针变量未初始化:指针变量在被创建未初始化时,并不是空指针,它的缺省值是随机的,会乱指一气。所以指针变量在创建同时就应对其进行初始化,要么将指针设置为NULL,要么让其指向一个合法的内存。
    • 指针释放之后未置空:有时指针在free或者delete之后未赋值NULL,有可能被误以为是合法的指针,不能进关注free和delete后的指针名,他们只是将指针所指向的内存空间释放掉而已,但并没有把指针自身消灭,此时,指针指向的就是“垃圾”内存。被释放掉内存空间的指针应该立即将其置为NULL,防止产生野指针。
    • 指针操作超越变量作用域
class A {
public:
	void Func(void){ cout << “Func of class A<< endl; }
};
class B {
public:
	A *p;
	void Test(void) {
		A a;
		p = &a; // 注意 a 的生命期 ,只在这个函数Test中,而不是整个class B
	}
	void Test1() {
		p->Func(); // p 是“野指针”
	}
};

由于a的生命周期只是在void Test(void)函数内部,函数结束时a将被析构,所以在函数外使用指针p指向的内存空间已经被释放了,所以p已经是野指针了。

总之,良好的编程习惯可以有效避免这些问题的出现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值