内存的管理方式
1、内存的区域
对于内存的区域划分上,不同的区域划分上都各有不同。
划分1:
代码区、堆、栈、 全局区(静态存储区)、 文字常量区、
划分2:
代码段、堆、栈、 data段、BSS段、文字常量区
全局区:
又成为静态存存储区。保存的是全局变量和静态变量(带有static 关键字)。全局区分为两个区域:一个区域保存的是经过初始化,且初始化的值不为零的全部变量和静态变量;一个区域保存的是没有经过初始化或者初始化的值为零的。程序结束由 OS 进行释放
常量区:
将一些不可以被更改的只读的常量进行保存的区域。比如文字字符串等。程序结束之后由系统释放。
代码区:
保存的是二进制代码的区域。
堆:
由程序猿手动 malloc/free进行开辟的空间,一般也是由程序猿调用 free/delete 进行释放,即使没有进行释放,也可以由 OS 进行释放。
栈:
程序运行的时候由编系统进行分配,存在函数的的局部变量等。程序结束由系统自动释放。
DATA段:
有经过初始化的全局变量和静态变量存储的区域,当然初始值不能为零
BSS段:
保存的是没有经过初始化的全局变量和静态变量存储的区域,或者经过初始化但是初始值为零的也保存在这个区域。
注意:很显然,DATA 段和 BSS 段的也行,其实就是全局区(静态存储器)内部之一,DATA 段和 BSS 段只不过是全局区更加精细的划分。
解释:
借助前人总结的知识:
int a = 0; 全局初始化区 char *p1; 全局未初始化区 main() { int b; // 栈 char s[] = "abc"; //栈 char *p2; //栈 char *p3 = "123456"; //"123456/0"在常量区,p3在栈上。 static int c =0; //全局(静态)初始化区 p1 = (char *)malloc(10); p2 = (char *)malloc(20); //分配得来得10和20字节的区域就在堆区。 strcpy(p1, "123456"); // 123456 放在常量区,编译器可能会将它与p3所指向的"123456" //优化成一个地方。 }
2、内存的三种来源:栈、堆、全局区
栈:
(1)运行时候由编译器自动分配自动回收,这一切都是自动的,程序猿不用管理
(2)反复使用:每一个进程,操作系统都会为这个进程分配栈,这个进程不论是怎么出入栈,都是在这个栈,反复使用。
(3)脏内存:栈内存由于反复的使用,程序使用完毕之后不会去做清理的工作,所以重新使用栈的时候,值就是脏的。
(4)临时性:局部变量的出栈入栈,每次都是不一样,所以都是临时性的,所以,绝对不要返回栈变量的指针,因为栈地址都是临时的,
(5)栈溢出:因为栈的大小是操作系统设定的,当出现无线递归或者出现巨大的局部变量。
堆:
(1)大块内存:栈的空间非常有限,所以当需要需要大块内存的时候,就在堆进行申请,
(2)手动申请、释放: 使用 malloc/new 申请,free/delete 进行释放。
(3)脏内存 : 堆内存也是被反复使用的
malloc 实际应用:
操作系统会对空闲的内存块进行组织管理,而这种组织管理的方式是以链表的形式。当程序猿调用 malloc 的时候就从空闲的链表找出一块大小满足申请要求的空间(可以比用户申请的大),然后将这个内存空间一分为二:一部分是用户申请空间的大小,另外的部分是维护链表这个节点的基本信息(比如地址、块内存的大小)。所以当 malloc 的时候,不论申请的空间是多大,都必须申请一块用于维护链表的空间。
#include <stdio.h> #include<stdlib.h> int main(void) { int i,j; FILE * fp = fopen("qxj511.txt", "w"); for ( i = 0; i < 2048; i++) { int *p1 = (int *)malloc(i); int *p2 = (int *)malloc(i); fprintf(fp, "i =%d : p1 = %d, p2 = %d,\n",i, p1, p2); free(p1); free(p2); } fclose(fp); /*关闭文件*/ }
在 gcc 的编译器下面做的测试,
i =0 : p1 = 151765360, p2 = 151765376, // 1 i =1 : p1 = 151765376, p2 = 151765360, i =2 : p1 = 151765360, p2 = 151765376, i =3 : p1 = 151765376, p2 = 151765360, i =4 : p1 = 151765360, p2 = 151765376, i =5 : p1 = 151765376, p2 = 151765360, i =6 : p1 = 151765360, p2 = 151765376, i =7 : p1 = 151765376, p2 = 151765360, i =8 : p1 = 151765360, p2 = 151765376, i =9 : p1 = 151765376, p2 = 151765360, i =10 : p1 = 151765360, p2 = 151765376, i =11 : p1 = 151765376, p2 = 151765360, i =12 : p1 = 151765360, p2 = 151765376, // 1 i =13 : p1 = 151765392, p2 = 151765416, // 2 i =14 : p1 = 151765416, p2 = 151765392, i =15 : p1 = 151765392, p2 = 151765416, i =16 : p1 = 151765416, p2 = 151765392, i =17 : p1 = 151765392, p2 = 151765416, i =18 : p1 = 151765416, p2 = 151765392, i =19 : p1 = 151765392, p2 = 151765416, i =20 : p1 = 151765416, p2 = 151765392, i =21 : p1 = 151765440, p2 = 151765472, i =22 : p1 = 151765472, p2 = 151765440, // 2 。。。。。。。
经过笔者的测试,当 申请的的空间,从零到12个字节的时候,两者的差是16个字节,后序申请的全部的空间差是 8 个字节。也就是说,实际申请的空间是链表头部 + 申请的空间,同时,8 个字节是处于内存对齐。
参考 : http://blog.csdn.net/misskissc/article/details/17717717
(1)malloc(0) :
If size is 0, thenmalloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
根据官方的解释,当申请的空间为零的时候,返回值要么是为 NULL,要么就可以正确返回一个地址,这个地址被正确释放。但是实际上,都是返回后者。
根据 malloc 的实现方式,虽然申请的空间为零,但是链表的指针也是会被申请一段空间的,所以可以正确申请,空间为 16 字节(maybe 8字节) + 0; 而这个链表指针地址其实就是 malloc 的返回值的地址。
注意: 对于链表维护空间的大小是16 字节,这个是不确定的,有人说是8个字节,
malloc 与 sizeof:
使用 malloc 是申请了一个程序猿指定的空间,返回值是指向这段空间的的首地址。想要计算这段申请空间的大小,是不可以通过 sizeof 计算出来的:
int *p = (int *)malloc(10 * sizeof(int)); printf("%d\n", sizeof(p));
打印出来:
等于4
原因分析,p 是指向申请空间的首地址,也就是说这个是地址,对于指针来说,不论指向的空间是多大,指针占据的就是四个字节。所以,sizeof 是不能计算 malloc。