由C语言代码(文本文件)形成可执行程序(二进制文件),需要经过编译-汇编-链接三个阶段。编译过程把C语言文本文件生成汇编程序,汇编过程把汇编程序形成二进制机器代码,链接过程则将各个源文件生成的二进制机器代码文件组合成一个文件。
C语言编写的程序经过编译-连接后,将形成一个统一格式的二进制可执行文件,这个格式是一个依照可执行文件格式的,可以被系统识别,并且加载到内存中执行的,它由几个部分组成。在程序运行时又会产生其他几个部分,各个部分代表了不同的存储区域:
静态区域(全局区域)
全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(RW data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(BSS)。 程序结束后有系统释放
文本段(Text)
通常代码段和只读数据段合成为文本段(Text), 包含实际要执行的代码(机器指令)和常量。它通常是共享的,多个实例之间共享文本段。文本段是不可修改的。
代码段(Code)
代码段由程序中执行的机器代码组成。在C语言中,程序语句进行编译后,形成机器代码。在执行程序的过程中,CPU的程序计数器指向代码段的每一条机器代码,并由处理器依次运行。
只读数据段(RO data,即常量区)
只读数据段是程序使用的一些不会被更改的数据,使用这些数据的方式类似查表式的操作,由于这些变量不需要更改,因此只需要放置在只读存储器中即可。
通常字符串常量就是放置在这里,程序结束后由系统释放。
注意:这个区域的存在与否,一直是一个争议的地方,但是我们这里认同是存在的,因为我们的程序中的确出现了与其他数据段不同的一块区域,但是往往很多时候大家把只读数据段(RO data)和下面的已初始化读写数据段(RW data)合成为数据段data,但是其实这个是不合适的,因为在执行过程中,这两个区域的读写权限是不同的,顾名思义,只读数据段(RO data)是只读的,而已初始化读写数据段是可读可写的。
已初始化读写数据段(RW data -- Initialized Data Segment)
已初始化数据是在程序中声明,并且具有初值的变量,这些变量需要占用存储器的空间,在程序执行时它们需要位于可读写的内存区域内,并具有初值,以供程序运行时读写。
未初始化数据段(BSS --Uninitialized Data Segment)
未初始化数据是在程序中声明,但是没有初始化的变量,这些变量在程序运行之前不需要占用存储器的空间。
Block Started by Symbol,BSS段的变量只有名称和大小却没有值
动态区域
堆(heap)
堆内存只在程序运行时出现,一般由程序员分配和释放。在具有操作系统的情况下,如果程序没有释放,操作系统可能在程序(例如一个进程)结束后回收内存。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
栈(stack)
栈内存只在程序运行时出现,在函数内部使用的变量、函数的参数以及返回值将使用栈空间,栈空间由编译器自动分配和释放。其操作方式类似于数据结构中的栈。
代码段(Code)、只读数据段(RO data)、读写数据段(RW Data)、未初始化数据段(BSS)属于静态区域。
堆和栈属于动态区域。
代码段(Text)、只读数据段(RO data)和初始化读写数据段(RW data)在程序链接后即产生,存在与可执行文件中
但是未初始化数据段(BSS)将在程序初始化的时候开辟,而堆和栈作为动态区域在程序运行的过程中分配和释放。
一个可执行程序分为映像和运行两种状态。在编译链接后形成的映像中,将只包含代码段(text)、只读数据段(RO data)和读写数据段(RW data)。在程序运行之前加载的过程中,将动态生成未初始化数据段(BSS),在程序运行时将动态生成堆(Heap)和栈(Stack)区域。
在系统中,可执行文件(或者程序)最终只有放置在内存中才能运行的,程序的几个段,最终也会转化为内存中的几个区域。
在内存中,从地地址向高地址,依次是只读段、读写段、未初始化代码段、堆区域和栈区域。只读区域即文本段(Text)包含了代码段(Code)和只读数据段(RO data),在内存区域中。
映像文件中,将包含代码段(code)、只读数据段(RO data)以及读写数据段(RW data),未初始化代码段(BSS)在程序初始化即加载时开辟,而堆栈段在程序运行时动态开辟。
对于程序运行过程中的内存使用,堆和栈一般是相向扩展的。堆的分配由程序来分配,但是栈是由编译器管理的。
示例代码
#include <stdio.h>#include <stdlib.h>#include <malloc.h>//http://tech.ccidnet.com/art/302/20070108/995995_1.html#define SHW_VAR_ADR(ID, I) \printf("the %20s\t is at adr:%p\n", ID, &I); //打印变量地址宏#define SHW_POT_ADR(ID, I) \printf("the %20s\t is at adr:%p\n", ID, I); //打印指针指向地址宏extern void afunc(void);/*extern etext, edata, end;*//**(1)全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(RW data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(BSS)。程序结束后有系统释放如下面(1).1 (1).2 (1).3所述**/// (1).1 -- 只要是静态变量,即使是局部的,也不存储在栈中,而是即存储在静态区域中,并依据其是否初始化,分别存储在BSS段和DATA段static int /*bss_*/unini_glo_sta_var; // 静态未初始化全局变量,虽然系统会默认初始化为0,但仍然存放在BSS区static int /*data_*/ini_glo_sta_var = 10; // 静态初始化全局变量,存放在RW DATA区// (1).2 -- 只要是全局变量,即存储在