C语言的内存管理(代码段、数据段,栈,堆)

C语言的内存管理(代码段、数据段,栈,堆

C语言程序分为映像和运行两种状态,经过编译连接后形成的映像由:代码段(text) 数据段(data 包括RO data ,RW data), 其他段(调试的段,动态库共享库链接表的段)组成。可执行文件在内存运行时由栈,堆,数据段(由三部分部分组成:只读数据段,已经初始化读写数据段,未初始化数据段即BBS)和代码段组成,如下图所示:

C语言的内存管理(代码段、数据段,栈,堆)_第1张图片

C语言的内存管理(代码段、数据段,栈,堆)_第2张图片

 

  1. 栈区(stack):由编译器自动分配释放,存放函数的参数值局部变量等值。其操作方式类似于数据结构中的栈。
  2. 堆区(heap):一般由程序员分配释放,若程序员不释放,则可能会引起内存泄漏。注堆和数据结构中的堆栈不一样,其类是与链表。
  3. 程序代码区:存放函数体的二进制代码。
  4. 数据段:由三部分组成:

1>只读数据段

只读数据段是程序使用的一些不会被更改的数据,使用这些数据的方式类似查表式的操作,由于这些变量不需要更改,因此只需要放置在只读存储器中即可。一般是const修饰的变量以及程序中使用的文字常量一般会存放在只读数据段中。

2>已初始化的读写数据段

已初始化数据是在程序中声明,并且具有初值的变量,这些变量需要占用存储器的空间,在程序执行时它们需要位于可读写的内存区域内,并且有初值,以供程序运行时读写。在程序中一般为已经初始化的全局变量,已经初始化的静态局部变量(static修饰的已经初始化的变量)

3>未初始化段(BSS)

未初始化数据是在程序中声明,但是没有初始化的变量或者初始化为0,这些变量在程序运行之前不需要占用存储器的空间。与读写数据段类似,它也属于静态数据区。但是该段中数据没有经过初始化或者初始化为0。未初始化数据段只有在运行的初始化阶段才会产生,因此它的大小不会影响目标文件的大小。在程序中一般是没有初始化的全局变量和没有初始化的静态局部变量或者初始化为0

 

注意:bbs段不影响a.out的大小,bbs段的数据用占位符标示而已,不在a.out里面.运行才产生.stack heap也都是在运行的时候产生。

例一:静态存储区与栈区

void main(void)

{

char* p = “Hello World1”;
char a[] = “Hello World2”;
p[2] = ‘A’;
a[2] = ‘A’;
char* p1 = “Hello World1”;

}

这种写法相当于

const char ro[] = {Hello World1”};

char *p = a;

 

      这个程序是有错误的,错误发生在p[2] = ‘A’这行代码处,为什么呢,是变量p和变量数组a都存在于栈区的(任何临时变量都是处于栈区的,包括在main()函数中定义的变量)。但是,数据“Hello World1”和数据“Hello World2”是存储于不同的区域的。
      因为数据“Hello World2”存在于数组中,所以,此数据存储于栈区,对它修改是没有任何问题的。因为指针变量p仅仅能够存储某个存储空间的地址数据“Hello World1”为字符串常量,所以存储在静态存储区。虽然通过p[2]可以访问到静态存储区中的第三个数据单元,即字符‘l’所在的存储的单元。但是因为数据“Hello World1”为字符串常量,不可以改变,所以在程序运行时,会报告内存错误。并且,如果此时对p和p1输出的时候会发现p和p1里面保存的地址是完全相同的。换句话说,在数据区只保留一份相同的数据

 

例二:栈区与堆区
char* f1()
{
    char* p = NULL;
    char a;
    p = &a;
    return p;
}
char* f2()
{
    char* p = NULL:
    p = (char *)malloc(10*sizeof(char));
    return p;
}

      这两个函数都是将某个存储空间的地址返回,二者有何区别呢?f1()函数虽然返回的是一个存储空间,但是此空间为临时空间。也就是说,此空间只有短暂的生命周期,它的生命周期在函数f1()调用结束时,也就失去了它的生命价值,即:此空间被释放掉。所以,当调用f1()函数时,如果程序中有下面的语句:
      char* p ;
      p = f1();
      *p = ‘a’;

      此时,编译并不会报告错误,但是在程序运行时,会发生异常错误。因为,你对不应该操作的内存(即,已经释放掉的存储空间)进行了操作。但是,相比之下,f2()函数不会有任何问题。因为,malloc这个命令是在堆中申请存储空间,一旦申请成功,除非你将其delete或者程序终结,这块内存将一直存在。也可以这样理解,堆内存是共享单元,能够被多个函数共同访问。如果你需要有多个数据返回却苦无办法,堆内存将是一个很好的选择。但是一定要避免下面的事情发生:
    void f()
    {
        
        char * p;
        p = (char *)malloc(10*sizeof(char));
        
    }

这个程序做了一件很无意义并且会带来很大危害的事情。因为,虽然申请了堆内存,p保存了堆内存的首地址。但是,此变量是临时变量,当函数调用结束时p变量消失。也就是说,再也没有变量存储这块堆内存的首地址,我们将永远无法再使用那块堆内存了。但是,这块堆内存却一直标识被你所使用(因为没有到程序结束,你也没有将其delete,所以这块堆内存一直被标识拥有者是当前您的程序),进而其他进程或程序无法使用。我们将这种不道德的“流氓行为”(我们不用,却也不让别人使用)称为内存泄漏。

 

 

/*****************************************************************************/

const char ro[] = {this is only-read data};

const char* prtconst = constant data;

static char rw1[] = {this is global read-write data};

char bss_1[100];

Int main()

{

   short b;

   char a[100];

   char s[] = abcd

   Char *p1;

   char *p2 = 123456;

   Static char rw2[] = {this is local read-write data};

   Static char bss_2[100];

   Static int c = 0;

   P1 = (char *)malloc(10*sizeof(char));

   Strcpy(p1,xxxxxxxx);

   Free(p1);

   Return 0;

}

/*****************************************************************************/

 

你可能感兴趣的:(C语言的内存管理(代码段、数据段,栈,堆))