C语言内存布局

一.C程序的基本组成及产生过程

一个程序本质上都是由 BSS 段、data段、text段三个组成的。

C程序编译的完整过程:C源代码---->预编译处理(.c---->编译、优化程序(.s.asm---->汇编代码(.obj.o.a.ko---->链接程序(.exe.elf.axf 等)

程序编译后生成的目标文件至少含有这三个段,这三个段的大致结构图如下所示:

C语言内存布局_第1张图片

其中.text即为代码段,为只读;.bss段包含程序中未初始化的全局变量和static变量。data段包含三个部分:heap(堆)、stack(栈)和静态数据区。


二.程序运行时各部分详解介绍

C语言内存布局_第2张图片

1.代码段:在采用段式内存管理的架构中,代码段(text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域属于只读。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

2. 数据段:在采用段式内存管理的架构中,数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。数据段又细分为以下两种情况:

a.只读数据段(RO data.rodata

 只读数据段由程序中所使用的数据产生,该部分数据的特点是在运行中不需要改变,因此编译器会将该数据段放入只读的部分中。C语言中的只读全局变量,只读局部变量,程序中使用的常量等会在编译时被放入到只读数据区。

注意:定义全局变量const char a[100]={"ABCDEFG"};将生成大小为100个字节的只读数据区,并使用“ABCDEFG”初始化。如果定义为:const char a[ ]={"ABCDEFG"};则根据字符串长度生成8个字节的只读数据段(还有’\0’),所以在只读数据段中,一般都需要做完全的初始化。

b.已初始化读写数据段(RW data.data

读写数据段表示了在目标文件中一部分可以读也可以写的数据区,在某些场合它们又被称为已初始化数据段,这部分数据段和代码段,与只读数据段一样都属于程序中的静态区域,但具有可写性的特点。通常已初始化的全局变量和局部静态变量被放在了读写数据段。

如: 在函数中定义static char b[ 100]={“ABCDEFG”};读写数据区的特点是必须在程序经过初始化,如果只定义,没初始值,则不会生成读写数据区,而会定位为未初始化数据区(BSS)。如果全局变量(函数外部定义的变量)加入static修饰,这表示只能在文件内使用,而不能被其他文件使用。

3. 未初始化数据段BSS:在采用段式内存管理的架构中,BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

4.堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

5.栈 (stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变 量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

栈空间主要用于以下3数据的存储:1.函数内部的动态变量2.函数的参数3.函数的返回值。


三.在C语言的程序中,对变量的使用还有以下几点需要注意:

  1.函数体中定义的变量通常是在栈上,不需要在程序中进行管理,由编绎器处理。

  2.用malloc,calloc,realloc等分配内存的函数所分配的内存空间在堆上,程序必须保证在使用free释放,否则会发生内存泄漏。

  3.所有函数体外定义的是全局变量,加了static后的变量不管是在函数内部或外部都放在全局区。

  4.使用const定义的变量将放于程序的只读数据区。

四.针对程序执行过程说明 

   当程序在执行时动态分配空间(C中的malloc函数),所分配的空间就属于heap。其概念与数据结构中“堆”的概念不同。

   stack段存放函数内部的变量、参数和返回地址,其在函数被调用时自动分配,访问方式就是标准栈中的LIFO方式。(因为函数的局部变量存放在此,因此其访问方式应该是栈指针加偏移的方式,否则若通过push、pop操作来访问相当麻烦)

   data段中的静态数据区存放的是程序中已初始化的全局变量、静态变量和常量。

   在采用段式内存管理的架构中(比如intel的80x86系统),BSS 段(Block Started by Symbol segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域,一般在初始化时 BSS 段部分将会清零。BSS 段属于静态内存分配,即程序一开始就将其清零了。

   比如,在C语言之类的程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。

   text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而BSS段不在可执行文件中,由系统初始化。  

   BSS段只保存没有值的变量,所以事实上它并不需要保存这些变量的映像。运行时所需要的BSS段大小记录在目标文件中,但BSS段并不占据目标文件的任何空间。

C语言内存布局_第3张图片

举个例子加固理解:

 

int a = 0; //全局初始化区,。data段
  static int b=20; //全局初始化区,。data段
  char *p1; //全局未初始化区 .bss段
  const int A = 10; //.rodata段
  void main(void)
  {
  int b; //栈
  char s[] = "abc"; //栈
  char *p2; //栈
  static int c = 0; //全局(静态)初始化区 .data段
  char *p3 = "123456"; //123456\0在常量区,p3 在栈上。
  p1 = (char*) malloc(10);//分配得来的10和20个字节的区域就在堆区
  p2 = (char*) malloc(20);
  strcpy(p1, "123456"); //123456\0 在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方
}

代码段、只读数据段、读写数据段、未初始化数据段属于静态区域,而堆和栈属于动态区域。代码段、只读数据段和读写数据段将在链接之后产生,未初始化数据段将在程序初始化的时候开辟,而堆和栈将在程序的运行中分配和释放。C语言程序分为映像和运行时两种状态。在编译-连接后形成的映像中,将只包含代码段(Text)、只读数据段(RO Data)和读写数据段(RW Data)。在程序运行之前,将动态生成未初始化数据段(BSS),在程序的运行时还将动态形成堆(Heap)区域和栈(Stack)区域。一般来说,在静态的映像文件中,各个部分称之为节(Section),而在运行时的各个部分称之为段(Segment)。如果不详细区分,可以统称为段。

 

 

 

你可能感兴趣的:(C语言)