众所周知,我们在编程的时候经常会在函数中声明局部变量(包括普通类型的变量、指针、引用等等)。
同时,为了满足程序功能的需要,函数的返回值也经常是指针类型或是引用类型,而这返回的指针或是引用也经常指向函数中我们自己声明的局部变量。
这样,程序在某些情况下就可能存在一定的问题。看似很简单的问题,通过仔细的分析,我们就能够更好的理解C++中内存分配和释放的问题。
好,废话不多说,我们进入正题。首先,简单介绍一下程序的内存区域的分配:
程序的内存分配
①堆区(heap)。这一部分主要是由程序开发人员自己通过new和malloc等操作创建的“对象”所保存的内存区域,该区域的变量需要开发人员自己释放。存储方式类似链表。
②栈区(stack)。这一部分主要是程序中的函数所以及其中的变量所占用的内存区域(包括了main函数)。存储方式类似栈。
③全局区或常量区(static)。这一部分主要是存放全局变量、静态数据、常量。程序结束后由系统释放。
④文字常量区。这一部分除妖存放常量字符串。 程序结束后由系统释放。
⑤程序代码区。程序的二进制代码。
1、函数中声明普通变量
void func1()
{
int i;
}
在函数func1()中,我们声明了一个int变量i,它保存在栈区中,当func1执行完成后,i也将被释放。
2、函数中声明指针类型变量
void func2()
{
int *p = (int*)malloc(sizeof(int));
A *a = new A; //A是一个类
}
在函数func2()中,我们声明了一个int型指针变量p和一个用户自定义类A指针变量a,它们都保存在堆区中,在函数func2执行完成后,仍然不会被释放,需要程序开发人员自己使用free()或delete等等进行释放。否则就会导致内存泄漏的问题。
3、函数返回值为指针或引用类型
函数的返回值为指针和引用类型是非常常见而且实用的,如果我们的函数返回的指针或者引用来自函数中声明的指针类型变量,则没有问题。如下:
int* CreatePointerFactory()
{
int *p = (int*)malloc(sizeof(int));
//to do sth here
return p;
}
这样的代码也是常见的简单工厂模式。但是需要注意的是,在通过函数CreatePointerFactory得到指针之后,在完成了相关操作之后,要进行对应的内存释放工作。最后需要将指针的值设置为NULL,防止野指针的产生。
第二种情况,函数返回的指针或者引用来自函数中声明的普通类型变量,则会产生非常严重的问题。如下:
int* getPointer()
{
int i = 1;
//to do sth here
return &i;
}
//main.cpp
int* p = getPointer();
因为变量i在栈区创造,在getPointer函数执行结束之后,这片内存区域将被释放,变量i所对应的内存也将被释放掉。因此,如果我们在其他某处(如主函数中)调用了该函数,那么返回得到的指针则是非常危险的,指向的位置是没有任何意义的。
但是,在做实验的时候,我们会发现,此时在某些编译器下,打印*p的值,可能仍是1,有的也可能是乱码。
这里的原因主要如下:
①不同的编译器处理和优化不同。
②OS在释放了这一部分内存之后,并不是完全清空内存中的数据,而只是将标志位flag只为true,表示此块内存是未被占用的。