一、内存空间结构
栈区:自动存放变量。在执行函数时,编译器会将函数的参数,局部变量自动存放在栈内,函数调用结束时会自动回收空间,栈内存分配效率高但容量有限。栈是又高地址向低地址生长,向下生长的。
堆区:在运行时调用程序来分配内存(malloc/new),由程序员自己决定分配内存的大小,同时需要程序员自行释放分配的空间(free/delete)。
数据段:内存在程序启动的时候才被分配,而且可能直到程序开始执行的时候才被初始化,如函数中的静态变量就是在程序第一次执行到定义该变量的代码时才被初始化。所分配的内存在程序的整个运行期间都存在,如全局变量,static变量等。
代码段:存放常量,不允许被修改。
二、C动态内存管理
C语言使用malloc/calloc/realloc/free进行动态内存管理。
1、malloc:在内存中开辟一块指定大小连续的空间,将空间首地址给指针变量
(1)函数声明:extern void *malloc(unsigned int num_bytes);
(2)函数的使用
#include<stdio.h> #include<malloc.h> int main(void) { char* p = (char*)malloc(100 * sizeof(char)); if(p != NULL) { /*...*/ free(p); } return 0; }
说明:传参部分为所需要申请的内存空间的大小。
返回值为void* ,因此需要强制类型转换为需要的类型。
P是:在栈内存中存放了一个指向一块堆内存大小为100字节的指针。
如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。
2、calloc:在内存的动态存储区中分配n个长度为size的连续空间
(1)函数声明:extern void*calloc(size_t n,size_t size);
(2)函数的使用
#include<stdio.h> int main(void) { char* p = (char*)clloc(100,sizeof(char)); if(p != NULL) { strcpy(p,"hellow world"); free(p); } return 0; }
说明:与malloc相似。
3、realloc:用于修改一个原先已经分配内存块的大小,可以扩大也可以缩小。
(1)函数声明:void *realloc(void *mem_address, unsigned int newsize);
(2)函数的使用
说明:#include<stdio.h> #include<malloc.h> int main(void) { char* p = (char*)malloc(100 * sizeof(char)); if (p != NULL) { p = (char*)realloc(p,150 * sizeof(char)); /*...*/ free(p); } return 0; }
扩大内存时:先判断当前的指针是否有足够的连续空间,如果有,扩大p指向的地址,并且将返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用(free),同时返回新分配的内存区域的首地址)。
若缩小时:该内存块尾部的部分内存释放,其余部分保留。
4、free:释放ptr指向的存储空间。被释放的空间通常被送入可用存储区池,以后可在调用malloc、realloc以及 calloc函数来再分配。
函数声明: void free(void *ptr)
三、C++动态内存管理
C++通过new和delete动态管理内存。
new/delete动态管理对象。new[]/delete[]动态管理对象数组。
1、new/delete
new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象都是通过该指针来间接操作的,动态创建的对象本身没有名字。
int main(void) { int* p4 = new int; //动态分配4个字节(1个int)的空间单个数据 int* p5 = new int(3); //动态分配4个字节(1个int)的空间并初始化为 int* p6 = new int[3]; //动态分配12个字节(3个int)的空间 delete p4; delete p5; delete[] p6; return 0; }
注意:释放数组空间时delete后面的 [] 是很重要的,如果少了 [] ,则编译器会认为该指针是指向数组第一个元素的,会产生回收不彻底的问题(只回收了第一个元素所占的空间),加了 [] 后就转化为指向数组的指针,回收整个数组。delete[] 中不需要填写数组元素的个数,编译器自知,即使填了,编译器也会忽略。
例:
int main(void) { int* a[2]; a[0] = new int[3]; a[1] = new int[3]; //注意这里也是一个数组,却不能使用delete[]; delete a[0]; delete a[1]; return 0; }
2、new/delete 的深度解剖
简单用图表示:
注意:
new[]:完成两件事,先底层调用malloc分了配内存,然后创建一个对象(调用构造函数)。
delete[]:也完成两件事,先调用析构函数(清理资源),然后底层调用free释放空间。
C++动态内存管理