C++内存管理
2013-04-09 20:48 夜与周公 阅读(291) 评论(0) 编辑 收藏 举报一直都想总结一下C++内存管理这一块,因为我发现C++编程的很多错误都是源自对C++内存管理理解的不够透彻。可真要总结的时候,才发现思绪万千,不知从何着手。于是乎网上搜了一些相关博文,加之查阅了一些书籍,比较清晰理出头绪来。
1.C++程序的内存格局
C++程序的内存占用可以分为以下五种:
(1)全局变量、静态变量数据区:全局变量,static变量。在main()之前已配置好,在整个程序的生命周期,一直存在。
(2)常量数据区:存储程序的常量数值、字符串等。在main()之前已配置好,在整个程序的生命周期,一直存在。
(3)栈数据区:函数执行时,存储程序的局部变量,保存传递参数等,函数调用结束时变量空间自动被释放
(4)堆数据区:动态内存,由程序员自己开辟的内存空间,也由程序员自己控制释放。在整个程序运行结束后,被系统自动释放。
(5)代码区:存在C++代码的存储空间,在程序运行前被操作系统装载,在整个程序运行结束后,被系统自动释放
2.C++变量的生存空间与生存范围
C++变量的生存空间可以分成三种:
(1)file variable:文件变量,定义变量在函数外面。从声明开始到文件尾部变量可见,存放于全局变量数据区。
(2)local variable: 局部变量,函数内部定义或者使用的变量。仅函数内部可见,存放于栈数据区。
(3)static variable :静态变量,定义在函数外部,与文件变量作用相同,定义在函数内部,与局部变量作用相同。存放在全局变量数据区。
(4)dynamic variable :动态变量,由程序员控制其生命周期。属于堆数据区。
3.C++内存常见操作错误
C++内存的有关操作常常与指针结合在一起,然而,由于指针的灵活性,可能会引起一些很难发现的错误。下面从内存的开辟、内存的使用与内存的释放,三个方面总结常见的一些问题。
3.1 内存的开辟
第一种开辟file variable 或者 local varialbe:
const int size = 100; char my_array[size];
注意这部分的内存是由操作系统自动开辟与释放的,编译器需要知道开辟内存的具体大小,因此,上面代码中size的类型必须是常量,否则编译器会报错。
第二种,采用动态方式开辟内存。在C里面用malloc()函数开辟内存,C++使用new操作符开辟内存。
int *p =new int[size];
在开辟的内存后,注意需要检查指针p是否为空。系统内存分配失败,指针p=NULL。一般情况下,理论上使用的堆内存最大是4G(32位寻址),出现系统内存分配失败的可能性很小。但是如果,在我们程序中,大量的动态开辟内存,可能会出现这种错误(呜呜,我还没遇见过这种情况)。总之,万事小心为上吧。
3.2 内存的使用
3.2.1 操作常量数据区的内存
常量数据取的数据是程序执行前,已经配置好的并且不可改变,我们有时忘记了这点,而引起错误:
char *p = "I love you , Li"; cout<<*p; p[0] = 'i'; cout<<*p;
在vs2010下,编译程序提示内存访问冲突。
3.2.2 越界或者操作不存在的内存数据
可以分为以下几种情况:
(1)操作数组越界,初学C/C++可能会在这个地方发错,因为C/C++数组索引从0开始,在长度为Size的数组,我们可以访问的空间:[0,size)。
(2)操作函数内已经被自动释放的内存(栈内存),如下:
char* funA( ) { char p[] = "l love lili"; return p; } int main() { char *p=funA(); cout<<p; return 0; }
当前这个程序,funA()返回了函数的栈内存,但当函数生命周期结束后,栈内存会被自动释放,在VS2010运行环境下,当前程序编译并没有提示错误,但是输出的却是乱码。
(3)操作野指针。
所谓野指针是指那些指向不可用的内存空间的指针。这种情况出现在,1)没有将指针初始化为NULL或者指向有意义的空间; 2)free 或者delete动态开辟的空间后,没有将样本的空间指针赋值为NULL。对于这样的错误,编译器是无法发现错误的。野指针空间里面存放着存在某个空间的地址,而这个地址上内容是不可知的,将会造成程序不可预知的错误。
3.2.3操作指针参数(函数之间传递指针参数)(摘自林锐<<高质量C++>>)
如果函数的参数是一个指针,不要期望用它申请空间:
void GetMemory(char *p ,int num) { p = new int[num]; } void Test() { char *str=NULL; GetMemory(str, 100); //参数传递,采用的是值传递,会对每个参数建立一个副本,因此仍有str=NULL strcpy(str,"hello"); //Error ! }
解决方法:1)传入二级指针:
void GetMemory2(char **p ,int num) { *p = new int[num]; }
2)return 堆内存:
void GetMemory3(cint num) { int *p = new int[num]; return p; }