动态分配内存
1动态分配内存_mallloc_free
困境
(回础10.4讲)
需要用一个数组来保存用户的输入,但是却不知道用户会输 入多少条数据。
(1) 如果设一个太大的数组,则显得浪费内存
(2) 如果设得太小,又怕不够
问题:如何做到恰好够用、又一点不浪费呢?
困境
C/C++里,要求数组长度为常量
int Contact[100]; // 长度必须在代码里固定
不能这样:
int n = 0;
scanf(“%d”, &n);
int Contact[n]; // 编译错误!!数组长度必须为常量
动态内存分配 – malloc/free
系统中存在一个内存管理器(MM, Memory Manager),它负责 管理一堆闲置内存。它被设计用于解决此类问题。
MM提供的服务:应用程序可以向MM申请(借出)一块指定大 小的内存,用完之后再释放(还回)。
例如,
void* ptr = malloc (1024); // 申请,从MM借出内存
free(ptr); // 释放,还回MM
动态内存分配 – malloc/free
malloc函数
void* malloc(int size)
参数size: 指定要申请的内存空间的大小
返回值: void* ,指向这一块内存地址
(MM不关心你拿这块内存来存储何种数据,所以返回void*)
应用程序在使用malloc时,要把返回值转换成目标类型。例如, 要申请一块空间存放1000个Contact对象,则
int size = 1000 * sizeof(Contact);
Contact* p = (Contact*) malloc(size);
这块内存和数组没有本质区别,用法完全相同。根据第9章,数 组本质上就是一块连续的内存,两者是一样的。
动态内存分配 – malloc/free
free函数
void free(void* ptr)
ptr: 先前malloc返回的内存地址
返回值: void* ,指向这一块内存地址
动态内存分配 – malloc/free
举例:
先计算需要多少字节的内存空间
char* p = (char*)malloc(8); // 申请8个字节
for(int i=0; i<0; i++)
{
p[i] = i + 1;
}
free§; // 释放
// 观察内存:p指向的内存里的数据是显而易见的
动态内存分配 – malloc/free
举例:
int size = 4 * sizeof(Contact);
Contact* p = (Contact*) malloc (size);
p[0].id = 1;
strcpy(p[0].name, “shaofa”);
free §;
动态内存分配 – malloc/free
举例:
// 用户自己决定要输入多少条记录
int n = 0;
scanf(“%d”,&n);
// 用户需要多少,就分配多少内存
int size = n * sizeof(Contact);
Contact* p = (Contact*) malloc(size);
// 释放
free(p);
// 观察内存:p指向的内存里的数据是显而易见的
关于MM
(1) MM是一个系统级的东西,所有的应用程序都向同一个MM申请内存。
(2) 何为借出?实际上,在内存被借出时,MM只是把它管理的内存标记 了一下,表示该段内存已经被占用。比如,它把每一段被占用的内存给 记录下来(首地址,长度) (p0,n0) (p1, n1) (p2, n2) …
(3) MM非常慷慨:①只要有人malloc,它都同意借出 ②你不归还,它 永远不会主动要求你free。
这意味着,用户程序应该自觉得及时free
,以便不耽误别的应用程序的 使用。如果有个应用程序不停地malloc,而不free,那最终会用光MM的 内存。当MM没有更多闲置内存时,malloc返回NULL,表示内存已经用完。
关于MM
MM管理的内存区域称为“堆”Heap
,相当于一个仓库。当用 程序要malloc时,就从仓库里登记借出。当free时,就将这 一块内存放回仓库。
再次重申:应用程序在malloc之后,应该尽早free!
关于MM
为何free的时候只需一个首地址呢?为什么不传递长度?
实际上,MM对借出的内存块进行标识 (p0, n0) (p1, n1) (p2, n2) … 它内部已经保证任意两块内存不会“交叠”,即不会重叠, 不会把一块内存同时借给两个应用程序使用。
所以,每块内存的首地址都是不同的,在free的时候只需要 指明首地址即可。
小结
- 使用malloc申请内存,具体申请多大的空间有你决定
- 使用free释放内存
- 使用原则:需要的时候再申请,不需要的时候立即释放
2malloc和free的具体用法举例
何为“对象”
对象指的一块内存
Contact a; // a是一个对象,即存放着一个对象的数据
Contact* p = (Contact*) malloc(sizeof(Contact));
确切地说:p指向了一个对象
malloc/free示例
示例:用Citizen表示一个市民,用Car表示一个辆车。他起初没 有车,但未来可能有一辆车。
struct Car
{
char maker[32]; // 制造商
int price; // 价格
};
struct Citizen
{
char name[32]; // 名字
int deposite; // 存款
Car* car; // NULL时表示没车
};
malloc/free示例
定义一个对象,开始没车
Citizen shaofa = { “shaofa”, 100, NULL };
后来,他可能买了一辆车
void buy(Citizen* owner)
{
// 创建一个对象
Car* car = (Car*) malloc(sizeof(Car));
strcpy(car->maker, “Chevrolet”);
car->price = 10;
// 保存此对象 (确切地说是记住了指针)
owner->car = car; // 有车了
owner->deposite -= car->price; // 钱没了
}
malloc/free示例
终有一天,这车会报废。。。
void discard(Citizen* who)
{
free(who->car); // 此对象被销毁
who->car = NULL; // 回到无车状态
}
malloc/free示例
也有可能会买给别人,
void sell(Citizen* owner, Citizen* other)
{
Car* car = owner->car;
car->price *= 0.5; //半价出售
other->car = car; // 别人拥有了这辆车
owner->deposite += car->price; // 收回一部分成本
//free(car); // oh,no! 不能free,这车在别人手里
owner->car = NULL; // 回到无车状态
}
注意事项
(1)不是malloc的指针,不可以free
例如,
int a = 10;
int* p = a;
free (p ); // 开什么玩笑??这个指针根本不是从MM那 里借来的
注意事项
(2) malloc的内存,必须及时free
怎么样才算“及时”?
“不及时”会怎样?
MM里可用的内存是有限的,你用完了就得尽快还,因为别的应用 程序也需要MM的内存。
只借不还,积累到一定程度,MM没有更多内存可用,于是malloc 返回NULL。
while(1)
{
void* ptr = malloc(1024*512);
}
注意事项
(3) 要free必须free首地址
错误的例子:
char* p = (char*) malloc(100); // 100个字节
free (p+50); // 借了100, 只还50?
要还就得全还,否则MM那边处理不了。(p, n)
注意事项
(4) malloc的返回值需要检测
char* ptr = (char*) malloc(1024); // 512K
if(ptr != NULL)
{
…
}
原因是:MM可能此时没有闲置内存可用。(虽然这种情况一 般不会发生)
注意事项
(5) free之后,该指针不应再使用
free之后,该内存交还给MM,该内存不再可用(失效)。
以下代码是错误的:
char* p = (char*) malloc(100);
free §;
for(int i=0; i<100; i++)
{
p[i] = i; //该内存已经被free,绝不可继续使用!
}
良好的编程习惯是:
free(p);
p = NULL; // 置为空指针
注意事项
(6) malloc得到的内存,可以任意位置释放
不一定要在相同的函数里释放,在应用程序的任意一个角落 释放都是有效的。
也就是说:这一块内存被malloc出来之后,完全交给你处置。