内存的管理方式

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。

posted @ 2015-11-03 16:55  qxj511  阅读(873)  评论(0编辑  收藏  举报