乐趣区

关于c:C语言编程程序的内存如何布局

一:C 语言程序的存储区域

由 C 语言代码 (文本文件) 造成可执行程序(二进制文件),须要通过编译 - 汇编 - 连贯三个阶段。编译过程把 C 语言文本文件生成汇编程序,汇编过程把汇编程序造成二进制机器代码,连贯过程则将各个源文件生成的二进制机器代码文件组合成一个文件。

C 语言编写的程序通过编译 - 连贯后,将造成一个对立文件,它由几个局部组成。在程序运行时又会产生其余几个局部,各个局部代表了不同的存储区域:

1. 代码段(Code 或 Text)

代码段由程序中执行的机器代码组成。在 C 语言中,程序语句进行编译后,造成机器代码。在执行程序的过程中,CPU 的程序计数器指向代码段的每一条机器代码,并由处理器顺次运行。

2. 只读数据段(RO data)

只读数据段是程序应用的一些不会被更改的数据,应用这些数据的形式相似查表式的操作,因为这些变量不须要更改,因而只须要搁置在只读存储器中即可。

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

已初始化数据是在程序中申明,并且具备初值的变量,这些变量须要占用存储器的空间,在程序执行时它们须要位于可读写的内存区域内,并具备初值,以供程序运行时读写。

4. 未初始化数据段(BSS)

未初始化数据是在程序中申明,然而没有初始化的变量,这些变量在程序运行之前不须要占用存储器的空间。

5. 堆(heap)

堆内存只在程序运行时呈现,个别由程序员调配和开释。在具备操作系统的状况下,如果程序没有开释,操作系统可能在程序 (例如一个过程) 完结后回收内存。

6. 栈(stack)

栈内存只在程序运行时呈现,在函数外部应用的变量、函数的参数以及返回值将应用栈空间,栈空间由编译器主动调配和开释。

C 语言指标文件的内存布局

看一个例子:

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 语言在编译和连贯后,将生成代码段 (Text)、只读数据段(RO Data) 和读写数据段 (RW Data)。在运行时,除了以上三个区域外,还包含未初始化数据段(BSS) 区域和堆 (Heap) 区域和栈 (Stack) 区域。

二:C 语言程序的段

1. 代码段(code 或 text)

代码段由各个函数产生,函数的每一个语句将最终通过编绎和汇编生成二进制机器代码(具体生生哪种体系结构的机器代码由编译器决定)。

2. 只读数据段(RO Data)

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

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

3. 读写数据段(RW Data)

读写数据段示意了在指标文件中一部分能够读也能够写的数据区,在某些场合它们又被称为已初始化数据段,这部分数据段和代码段,与只读数据段一样都属于程序中的动态区域,但具备可写性的特点。通常已初始化的全局变量和部分动态变量被放在了读写数据段,如:在函数中定义 static char b[100]={“ABCDEFG”}; 读写数据区的特点是必须在程序通过初始化,如果只定义,没初始值,则不会生成读写数据区,而会定位为未初始化数据区 (BSS)。如果全局变量(函数内部定义的变量) 退出 static 润饰,这示意只能在文件内应用,而不能被其余文件应用。

4. 未初始化数据段(BSS)

与读写数据段相似,它也属于静态数据区,然而该段中的数据没有通过初始化。因而它只会在指标文件中被标识,而不会真正称为指标文件中的一段,该段将会在运行时产生。未初始化数据段只在运行的初始化阶段才会产生,因而它的大小不会影响指标文件的大小。

在 C 语言的程序中,对变量的应用还有以下几点须要留神:

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

2. 用 malloc,calloc,realloc 等分配内存的函数所调配的内存空间在堆上,程序必须保障在应用 free 开释,否则会产生内存透露。

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

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

三:程序中段的应用

上面用一个简略的例子来阐明 C 语言中变量和段的对应关系。C 语言程序中的全局区(动态区),理论对应着下述几个段:RO Data; RW Data ; BSS Data.

一般来说,间接定义的全局变量在未初始化数据区,如果该变量有初始化则是在已初始化数据区(RW Data),加上 const 则将放在只读数据区。

const char ro[] = {“this is read only data”}; // 只读数据区

static char rw_1[] ={“this is global read write data”}; // 已初始化读写数据段

char BSS_1[100]; // 未初始化数据段

const char *ptrconst =”constant data”; // 字符串放在只读取数据段

int main()

{

short b; // 在栈上,占用 2 个字节

char a[100]; // 在栈上开拓 100 个字节,它的值是其首地址

char s[]=”abcdefg”; // s 在栈上,占用 4 个字节,”abcdefg” 自身搁置在只读数据存储区,占 8 个字节

char *p1; //p1 在栈上,占用 4 个字节

char *p2=”123456″; //p2 在栈上,p2 指向的内容不能改,“123456”在只读数据区

static char rw_2[]={“this is local read write data”};// 部分已初始化读写数据段

static char BSS_2[100]; // 部分未初始化数据段

static int c = 0; // 全局 (动态) 初始化区

p1=(char )malloc(10 sizeof(char) ); // 分配内存区域在堆区

strcpy(p1,”xxxx”); //“XXXX”放在只读数据区,占 5 个字节

free(p1); // 应用 free 开释 p1 所指向的内存

return 0;

}

读写数据段蕴含了忆初始化的全局变量 static char rw_1[]以及部分动态变量 static rw_2[]. 其差异在于编绎时,是在函数外部应用的还是能够在整个文件中应用。对于 rw_1[] 无论有无 static 润饰,其都将被搁置在读写数据区,只是是否被其它文件援用与否。对于后者就不一样了,它是部分动态变量,搁置在读写数据区,如果没 static 润饰,其意义齐全扭转,它将会是开拓在栈空间的局部变量,而不是动态变量,在这里 rw_1[],rw_2[]后没具体数值,示意动态区大小同前面字符串长度决定。

对于未初始化数据区 BSS_1[100]与 BSS_2[100],其区别在于前者是全局变量,在所有文件中都能够应用; 后者是局部变量,只在函数外部应用。未初始化数据段不设置前面的初始化数值,因而必须应用数值指定区域的大小,编绎器将依据大小设置 BSS 中须要减少的长度。

栈空间次要用于以下 3 数据的存储:

1. 函数外部的动静变量

2. 函数的参数

3. 函数的返回值

栈空间是动静开拓与回收的。在函数调用过程中,如果函数调用的档次比拟多,所须要的栈空间也逐步加大,对于参数的传递和返回值,如果应用较大的构造体,在应用的栈空间也会比拟大。

退出移动版