C语言精讲之程序中内存从哪里来(1)内存
C语言精讲之程序中内存从哪里来(2)栈
C语言精讲之程序中内存从哪里来(3)堆
C语言精讲之程序中内存从哪里来(4)数据段
3、堆
一、
堆具有操作系统堆管理器管理、大块内存、程序手动申请&释放、脏内存、临时性
(1)、
操作系统堆管理器管理
堆管理是操作系统的一个模块,堆管理内存分配灵活,按需分配。
(2)、
大块内存
堆内存管理者总量很大的操作系统内存块,各进程可以按需申请使用,使用完释放。
比如
:图书馆借书,你根据自己的需求,借相应的书籍,向图书馆申请,这期间别人借不了,等你使用完之后,再申请还书。
(3)、
程序手动申请&释放
手工的意思是需要写代码去申请malloc(或者相似malloc族)和释放内存free。
(4)、
脏内存
堆内存也是反复使用的,而且使用者用完释放前不会清除,因此也是脏内存。
比如
:一个人申请一个酒店房间以后,用完没有打扫,当下一个人使用的适合,酒店直接诶这个人分配了这个房间,而没有进行打扫,当这个人使用的时候就是脏的。
(5)、
临时性
堆内存只在malloc的free之间属于这个进程,而可以访问,在malloc之前和free之后都不能访问,否则会有不可预料的后果。
二、
堆内存使用范例
4.5.2.2、堆内存使用范例
(1)、原型 void *malloc(size_t size)
1)void *是一个指针类型,malloc返回的是一个void *类型的指针,实质上malloc返回的是堆管理器分配给我本次申请的那段内存空间的首地址(malloc返回的值其实是一个数字,这个数字表示一个内存地址)。
2)为什么要使用void *作为类型?主要原因是malloc()帮我们分配内存是只是分配内存空间,至于这段空间用来做存储什么类型的元素malloc是不关心的,由我们程序来自己决定。
(2)、什么是void类型。早期被翻译成空型,这个翻译不好,会误导人,void类型不表示没有类型,而是表示万能类型。void的意思就是这个数据的类型当前是不确定的,在需要的时候可以强制转换成其它任何一种确定类型的指针,也就是说这个指针可以指向任何类型的元素。通过头指针来访问malloc的数据。
(3)、malloc的返回值:成功申请空间后返回这个空间的指针,申请失败返回NULL。所以malloc后的内存指针一定要先检验是否为NULL。
(4)、malloc使用完之后一定要free释放,free(p);会告诉堆管理器这段内存我用完你可以回收了,堆管理器回收这段内存后,这段内存就不应该再去使用这段内存,因为释放后堆管理器就可能把这段内存分配给其它的进程,所以不应该再使用这段内存。
(5)、在调用free()归还这段内存之前,指向这段内存的指针P一定不能丢(也就是不能给P赋值)因为 一旦p丢失,malloc的这一段内存就消失了(内存泄漏),直到当前程序结束时操作系统才会回收这段内存。
三、
malloc的一些细节表现
(1)、
malloc(0)
malloc申请0个字节一般不会碰到这个需求,因为本来申请内存就是拿来用的。
如果真的malloc(0),返回的还是一个有效的指针吗?
答案是:实际上分配了32位机(0x10)16byte( 1B(byte,字节)= 8 bit)的一段内存并返回了这段内存的地址。这个答案并不是固定的,因为C语言并没有明确规定malloc(0)时的表现,由各malloc函数库的实现者来定义(也就是具体分配多少并不是统一的)
(2)、
malloc(4)
gcc中的malloc默认最小以16B(32位机)为分配单位,如果malloc小于16B的大小都会返回一个32字节的大小的内存,malloc实现时没有按照自己的分配而是允许一些的块内存的分配。(相当于规定了一个个大小不同的块内存,比如需要二分之一的方便面,人家只会卖给你一包)
(3)、
malloc(20)去访问第25、第250、第2500。。。会怎么样?
实战中:120字节处正确,1200字节处正确…往后继续访问总有一个数据会发生段错误,所以申请多少就应该用多少。不要去使用没有申请的内存,这不是一个好的习惯。
#include <stdio.h>
#include <stdlib.h>
//需要一个1000个int类型的元素数组
int main()
{
//第一步:申请和绑定
int *p=(int *)malloc(1000*sizeof(int));
//第二步:检验是否分配成功
if(NULL == p)
{
printf("malloc error\n");
return -1;
}
//第三步:使用申请到的内存
//p = &a; //如果在free之前给P另外赋值,那么malloc申请的那段内存就丢失掉了
//malloc后P和返回的内存相绑定,P是那段内存在当前进程的唯一联系人,
//相当于唯一一把钥匙
//如果P没有free之前就丢了,这段内存就永远丢点了,
//丢了的概念就是在操作系统中这段内存是当前进程拿着的,但是用不了
//所以你想申请新的内存来替换使用,这就是叫做程序“吃内存”,叫内存泄漏
*(p+0)=1;
*(p+1)=2;
printf("*(p+0)=%d\n",*(p+0));
printf("*(p+1)=%d\n",*(p+1));
//第四步:释放内存
free(p);
p=NULL;
}