代码改变世界

C++内存管理

2013-04-09 20:48  夜与周公  阅读(288)  评论(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;          
}