内存管理

1、内存分配方式:

  1. 栈区:由编译器负责内存空间的创建于释放,当函数执行时,函数内的局部变量存储空间都在栈上创建,函数执行结束后,栈空间存储单元自动被释放;栈的空间大小有限,过度使用会导致栈溢出;
  2. 堆区:由程序员自己负责存储空间的创建于释放,凡是由函数malloc或运算符new申请的动态内存都在堆区创建,其内存释放需要程序员自己手动用函数free或运算符delete·去释放。堆区内存空间容量很大,但是很难管理。
  3. 静态存储区(全局存储区):静态存储区在编译前已经存在,其生存周期为整个程序的执行期间。存放的是静态变量及全局变量。

2、常见的内存问题:

  1. 使用分配失败的内存空间:解决办法:由malloc或new分配的动态内存需要通过if(p!=NULL)进行检查、函数参数是指针的需要在函数入口位置用assert(p!=NULL)来进行检查;
  2. 内存分配成功尚未初始化就使用它:解决办法:将创建的指针指向合理的有效地址或赋值为NULL;(创建数组必须赋值)。
  3. 内存分配成功且已经初始化,但操作越过了内存的边界:解决办法:使用时牢记自己分配的内存空间大小;
  4. 忘记释放内存,导致内存泄漏:这种问题会导致每调用一次该函数丢失一块内存,最终会导致内存耗尽。解决办法,malloc与free成对出现、new与delete成对出现;
  5. 使用释放掉的存储:
  • 当动态创建的指针p赋值给另一个同类型的指针时,当在使用另一个指针时却释放掉了创建动态存储的指针;导致其操作的指针空间是不确定的;
  • 当free或delete释放掉创建的内存指针时,必须再将指针置为NULL,防止其成为野指针;
  • return语句返回栈区的指针或栈区的引用;

3、sizeof运算符:

  • sizeof(数组)= 数组的容量(字节数);//当数组是形参时会自动转化为指针其大小为指针的大小。
  • sizeof(指针类型) = 4(字节数);//32位操作系统的指针字节数是4,64位操作系统的指针字节数是8。

4、形参指针用于动态内存创建:

  • 形参为一级指针时 ,p = malloc(sizeof(T)).由于指针作为形参在函数调用时会发生指针参数的复制,相当于函数内的指针是原指针的副本即_p = p;当函数内修改了_p的值即修改了_p的指向空间地址。因此在_p上的操作不影响原p的值。
  • 形参为二级指针时,*p = malloc(sizeof(T)).由于指针作为形参在函数调用时会发生指针参数的复制,相当于函数内的指针是原指针的副本即_p = p;但是_p与p指向的内存空间相同,当修改*p的值时即修改的是指针指向内存空间的值,而不是指针本身的值。因此二级指针可用于动态创建内存并反回。

5、free与malloc:

在c中使用free与malloc释放及创建动态内存,其函数在stdlib.h头文件中。malloc动态申请内存需要指针申请内存的大小,一般用sizeof()运算符确定大小。由于其反回类型是void*需要通过强制类型转为为指针类型;动态内存创建完毕需要坚持内存是否创建成功,通过if(p==NULL)判断,创建失败则指针为NULL;动态内存使用完毕要记得通过free(p)来释放,释放完毕将指针置NULL;

6、new与delete:

在c++中既可以使用free与malloc又可以使用new与delete申请动态内存。当申请非内置数据类型的动态内存时必须使用new与delete。因为在申请非内置数据类型的内存空间是需要自动调用数据类型的构造函数,在对象要消亡时要自动调用其析构函数。因为free与malloc是函数,不在编辑器的控制权限之内。不能把执行的构造函数与析构函数强加于free与malloc。

new申请非内置数据类型的动态内存空间时只需要指定类型即可,编辑器会自动计算类型对象大小并调用构造函数初始化对象。且其反回类型与指针类型必须完全一致。当new创建内存失败时会自动抛出异常。

当创建多个数据类型对象时需要用p =new <class T>[sizeof()],释放时delete []p,[]表示将数组内的每个对象都调用一次析构函数;

7、杜绝野指针:

  • 指针创建时必须初始化,例如:char* p = NULL; char* q = (char*)malloc(sizeof(char));
  • 指针释放后需要置零,例如:free(q);或者delete q;然后q = NULL;
  • 指针操作超过了栈区变量的作用域,导致变量地址被自动撤销;
原文地址:https://www.cnblogs.com/dhhu007/p/13265397.html