程序编译的四个阶段,编译后各段组成及内存空间分区

(一)C语言编译的四个阶段
1.预处理(gcc -E xxx.c -o xxx.i
(1)宏定义替换:#define PI 3.1415926。
(2)头文件展开:搜索#include中指定的头文件,并将头文件中的内容拷贝到源文件中。
(3)注释删除等。
(4) #ifdef 等内容,完成条件编译内容的替换。
注:此阶段不检查c程序代码语法是否错误,生成.i文件。

2.编译(gcc -S xxx.i -o xxx.s
(1)此阶段检查语法问题;
(2)然后生成中间汇编代码,但是还不可执行,gcc编译的中间文件是[.s]文件。
第一个阶段和第二个阶段由gcc编译器完成。

3.汇编(gcc -c xxx.s -o xxx.o
(1)此阶段主要完成将 汇编代码.s 翻译成 机器码.o 指令,并将这些指令打包形成可重定位的目标文件,[.O]文件,是二进制文件。
(2)此阶段由 as汇编器 完成。

4.链接(gcc xxx.o -o xxx // -L -l[lib_name])
(1)此阶段完成程序中调用的各种函数跟静态库和动态库的链接,并将它们一起打包合并形成可执行文件.elf。
(2)此阶段由 ld链接器 完成。

注:
(1)gcc编译用到以下几个程序:C编译器gcc、汇编器as、链接器ld和二进制转换工具objcopy。
(2)nm:
(3)file,size
(4)objdump -D xxx.elf > xxx.asm
(5)objcopy binary

(二)程序的编译后组成部分
(1)linux下程序编译后,可执行.elf文件的内存布局(段指二进制格式文件中的一块区域):
(1)文本段(.text):或称代码段,通常是用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读。某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。属于CPU执行的机器指令部分( 存放函数体的二进制代码 )。
(2)数据段(.data):通常是用来存放程序中已初始化的全局变量的一块内存区域。一般常量字符串就是放在这里的,程序结束后由系统释放。只读数据段(只读常量区) 和数据段(全局变量区)统称为 数据段。
(3)bss段:未初始化的全局变量和static修饰的局部变量(初始化默认值为0);

注:
1)对于data段,保存的是初始化的全局变量和stataic的局部变量,直接载入内存即可。
2)text段保存的是代码直接载入。
3)BSS段从目标文件中读取BSS段大小,然后在内存中紧跟data段之后分配空间,并且清零(这也是为什么全局表量和static局部变量不初始化会有0值得原因)。

(2)执行可执行文件时,将可执行.elf文件从磁盘上加载到内存中,并分配虚拟内存。linux(32位)下映射4G虚拟内存:用户空间(0 - 3G) 和内核空间(3 - 4G)。
注:
0xffff ffff :4G
0xc000 0000:3G

(3)用户空间:动态存储区和静态存储区
C语言中存储区分类:
1)栈 :由编译器自动分配释放
2)堆 :一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收
3)全局区(静态区):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(data区),未初始化的全局变量和未初始化的静态变量,static修饰的局部变量在bss段。data和bss都属于全局区。
4)常量区(readonly):一个专门放字符串常量的地方。常量存储区里面的数据是放在代码段里的,不占内存,是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。常量字符串都存放在静态存储区,返回的是常量字符串的首地址

C++中存储区分类:
1)栈:就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
2)堆:就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
3)自由存储区:就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
4)全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
5)常量存储区:这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)

(4)动态存储区:栈(stack) 和 堆(heap),存放局部变量。
a.栈区:执行代码时,局部变量只有其所在的函数调用时,局部变量才由系统自动在栈上临时开辟内存空间,局部变量的生命周期随其所在的函数的结束而被系统自动释放内存空间。
b 堆区:malloc (头文件<stdlib.h>),free§;p = NULL;

(5)静态(全局区)存储区静态区(全局区)变量在编译时就已经分配好内存空间:链接后的文件未加载前还是存在磁盘上的,文件中的变量、函数的地址都是逻辑地址。当程序执行,即加载可执行文件到内存上时,会将逻辑地址映射到内存中,这时全局变量就会被加载到内存中的数据段(data)。
注:每次调用有全局变量的函数时,都会在上一次值的基础上累加运算,而局部变量则是重新开辟栈。

(6)备注:
(1)函数体中定义的变量通常是在栈上。
(2)用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。
(3)在所有函数体外定义的是全局变量,在全局区。
(4)加了static修饰符后,不管变量在哪里都存放在全局区(静态区)
(5)在所有函数体外定义的static变量,表示只在该文件中有效,不能被extern到别的文件使用
(6)在函数体内定义的static表示只在该函数体内有效
(7)另外,函数中的"hello world"这样的字符串常量存放在 常量区。
(8)全局变量和静态static修饰的变量,没有初始化会自动初始化为0。

(7)误区:
1)普通全局变量和静态(static)全局变量的区别
答:静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,普通的全局变量在各个源文件中都是有效的,可以extern引用。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在其它源文件中不能使用它。

2)static 局部变量和普通局部变量的区别
答:局部变量改变为静态static局部变量后,改变了它的存储方式即改变了它的生命周期。
static 局部变量只被初始化一次,下一次是依据上一次值。

3)static 函数与普通函数的区别
答:
a)static 函数与普通函数作用域不同,static函数仅作用在本文件。
b)只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。
c)对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件。
d)static 函数在内存中只有一份(.data),普通函数在每个被调用中维持一份拷贝。

发布了13 篇原创文章 · 获赞 0 · 访问量 133

猜你喜欢

转载自blog.csdn.net/m0_46170433/article/details/104861540