C程序-内存的管理

1.C语言结构

  C程序-内存的管理

   上图为一个可执行代码存储时的结构和运行结构的对照。

- 代码区(test segment):

    存放CPU执行的机器命令,通常代码区是可以共享的,(即另外的执行程序可以调用它)。代码区通常是只读的,使其只读的原因是防止程序意外的修改了它的指令,代码区还规划了局部变量的相关信息。

- 全局初始化区/静态数据区

    该区包含了在程序中明确被初始化的全局变量,静态变量和常量数据。

    eg:int a = 99;

    整型变量a根据其初始化数据区中。

    eg:static b = 100;

    这声明了b为一个静态变量。如果在任何函数体外声明,则表示一个全局静态变量,如果在函数体内部,则表示其为一个局部静态变量。如果在函数体前面加上static,则表示此函数只能在当前文件中调用。

- 未初始化数据区(BSS)

    存入全局未初始化变量,BSS区的数据在程序开始运行之前被内核初始化为0或者空指针NULL。

    eg : int a[];   a将存储到未初始化数据区。

2.一个C编译的程序占用的内存分别为代码区,初始化数据区,未初始化数据区,堆区和栈区5个部分。

- 代码区

    代码区指令根据程序设计流程一次执行,对于顺序指令,则只会执行一次(每个进程),如果反复,则需要跳转指令,如果进行递归,则需要借助栈来实现。代码区的指令中包括操作码和要操作的对象(或对象地址的引用),如果是立即数(如5),将直接包含在代码中,如果是局部数据,将在栈区分配空间,如果是BSS区和数据区,在代码中将同样引用该数据地址。

- 数据区

    值初始化一次,为初始化数据区,在运行时改变其值。

- 栈区(stack)

    由编译器自动分配释放,存放函数的函数值,局部变量的值等。其操作方式类似于数据结构中的栈,每当一个函数被调用,该函数返回地址和一些关于调用的信息,比如默写寄存器的类容,被存储到栈区。然后这个被调用的函数再为它的自动变量和临时变量在栈上分配空间,这就是c实现函数递归调用的方法,每执行一次递归函数的调用,一个新的栈框架就会被使用,这样这个新实例栈里的变量就不会和该函数的另一个实例栈里的变量混淆。

- 堆区(heap)

    用于动态内存分配,堆在内存中位于bss区和栈区之间,一般由程序员分配和释放,若程序员不释放,程序结束时有可能由os回收。

- 一个程序在运行过程中,代码是根据流程依次执行的,只需要访问一次,跳转和递归有可能使代码执行多次,而数据一般需要访问多次,因此需要单独开辟空间以方便访问和节约空间。

- 临时数据及需要再次使用的代码在运行时放入栈区中,生命周期短。

- 全局数据和静态数据在整个程序执行的过程中都需要访问,因此需要单独管理。

- 堆区由用户自由分配,以便管理。

eg :

            int a = 0;    //a在全局已初始化数据区

            char *p1;    //p1BSS区(未初始化全局变量)

            main()

            {

                int b;   //b在栈区

                char s[] = "abc";//s为数组变量,存储在栈区

                //"abc"为字符串常量,存储在已初始化数据区

                char *p1,p2;  //p1p2在栈区

                char *p3 = "123456"; //123456\0在已初始化数据区,p3在栈区

                static int c =0;  //C为全局(静态)数据,存在于已初始化数据区

                //另外,静态数据会自动初始化

                p1 = (char *)malloc(10);//分配得来的10个字节的区域在堆区

                p2 = (char *)malloc(20);//分配得来的20个字节的区域在堆区

                free(p1);

                free(p2);

            }

- 内存分配的方式  

    在c语言中,对象可以使用静态或动态的方式分配内存空间。

    静态分配:编译器在处理序源代码时分配(在程序执行之前就分配了,效率高)。

    动态分配:程序在执行时调用malloc库函数申请分配。

- 静态与动态内存分配的区别

    静态的对象是有名字的对象,可以直接对其进行操作。

    动态对象是没有名字的变量,需要通过指针间接的对它进行操作。

    静态对象的分配与释放由编译器自动处理。

    动态对象的分配与释放必须有程序员显式的管理,它通过malloc()和free()两个函数来完成。

- 堆和栈的区别

    - 管理方式的不同:栈编译器自动管理,无需程序员手动控制,而堆的空间的申请释放是由程序员控制的,容易产生泄露。

    - 空间大小的不同:栈是向低地址扩展的数据结构,是一块连续的数据区域,栈顶的地址和栈的容量大小是系统预先规定好的,当申请的空间超过栈的剩余空间时,将提示溢出。堆是向高地址扩展的数据结构,是不连续的内存区域,因为系统是用链表来存储空闲的内存地址,且链表的遍历方向是由低地址向高地址,堆获得的空间较灵活。

    - 是否产生碎片:对于堆频繁的使用malloc/free,会造成大量碎片,是程序效率降低。

    - 增长的方向: 向上,地址增加,栈向下,地址减小。

    - 分配方式的不同: 堆都是malloc函数动态申请分配的,栈的分配则由编译器完成。

    - 分配效率的不同: 栈是机器系统提供的数据结构,计算机会在底层提供支持;分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行。堆则是c函数库提供的,它的机制比较复杂。如分配一块内存,库函数会按照一定的算法在堆中搜索足够大的空间,若没有,就需要操作系统重新整理内存空间,然后返回。