33.0、C语言——C语言预处理(1) - 翻译环境详解

33.0、C语言——C语言预处理(1) - 翻译环境详解

程序的 翻译环境 和 执行环境

在ANSI C的任何一种实现中,存在两个不同的环境;

第 1 种是翻译环境,在这个环境中源代码被转换为可执行的机器指令;

第 2 种是执行环境,它用于实际执行代码;

详解:翻译环境 = 编译 + 链接

        在我们的项目中 可能会出现多个 源文件,那么每一个源文件都会被系统单独的当成一个单元去单独处理;

        源文件在经过 编译器 处理之后 会产生一个目标文件也就是 .obj 文件,然后等待每一个目标文件都通过 链接器链接库 处理之后,会将所有目标文件链接到一起,然后产生一个可执行程序 .exe;

        - 组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code);

        - 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序;

        - 链接器同时也会引入标准 C 函数库中任何被该程序所用到的函数,而且他可以搜索程序猿个人的程序库,将其需要的函数也链接到程序中;

那么其实编译又可以细分为三个过程 ->

        1. 预编译

        2. 编译

        3. 汇编

详细介绍:

其实这三个过程在我们的 vs编译器 中不太好体现出来,还是用 Linux 比较容易理解;

预处理阶段 ->    

        1. 预处理 选项 gcc -E test.c -o test.i 编译完成之后就停下来,预处理之后产生的结果都放在 test.i 文件中;

        那么在进行预编译处理的时候,相当于把 #include <xxx.h> 执行了,将所有的头文件包含的内容添加到了我们的 源文件 中; 

        其实不仅仅只是将头文件包含进了源文件,还做了一些其他的事情,比如说 对源文件的注释删除(使用空格去替换注释),因为注释的信息对编译器来说没有任何意义;

        还有比如说 #define 定义的一些常量会在预编译阶段,将这些常量全部替换成 值 ,比如说 #define max 100 那么在预处理阶段会将程序中 所有的 max 换成 100;

        其实总的来说,就是一些文本的操作,文本的包含文本的替换啥的;

编译阶段 ->

        2. 编译选项 gcc -S test.c 编译完成之后就停下来,结果保存在 test.s 中;

        将我们的 C代码 翻译成了 汇编代码,那么详细的的来说就是做了以下一些动作 ->

        1. 语法分析

        2. 词法分析 ( 涉及到编译原理,这里不细说 )

        3. 语义分析

        4. 符号汇总 (比如说一些 函数名、全局变量等都汇总起来)

汇编阶段 ->

        3. 汇编 gcc -c test.c 汇编完成之后就停下来,结果保存在test.o 中;

        将之前编译好的汇编代码,转换成 object 目标文件【  当然这个目标文件我们依然看不懂,因为他是将汇编代码转换成了二进制指令 / 代码 】;然后所有的目标文件通过链接器生产可执行程序; 

        当然汇编阶段还做了一件事情 -> 形成符号表 ,符号就是之前在编译阶段汇总的符号,那么在汇编阶段会将符号以及符号对应的地址存储到符号表中;

例如像以下代码 ->

这里要汇总的符号有两个 一个是 main 一个是 add;

        在该源文件中我们确切的知道 main() 函数式存在的,所以可以有一个相对应的地址存到符号表中;但是 add() 函数只是声明,并不知道他是否确切存在,所以会存入一个无意义的地址;

链接阶段 ->

链接器要做以下两件事请 ->
1. 合并段表;

2. 符号表的合并和重定向;

1. 那么先来说一下什么是:合并段表 ->

        首先每个目标文件中会分为不同的段,每段会存放一些程序的数据、代码等;不过每个目标文件的段结构、格式是一样的【这种格式被称作 elf 文件格式,感兴趣的可以百度一下】,只是每段存放的东西不一样;

        经过链接阶段时,首先会把每个目标文件中相对应的段位置里的数据合并到一起,最终形成一个目标文件;

2. 什么是符号表的合并和重定向 ->

        在经过编译阶段后,会形成 符号表,符号表中存放了符号以及他们的地址;那么重复的符号会合并,合并之后如果他们的地址不同则取 有效地址 存入到合并后的符号表中;

符号表的作用是啥呢?

        比如说符号表里现在存放着一个函数 Add() 的 符号 Add 以及他的地址,但是这个函数根本就没有定义,只是声明了一下;那当我们在程序中调用这个 Add() 函数的时候,就会去符号表中查找有没有这个符号 Add,查找后发现确实有然后根据他存放的地址去调用Add() 函数,但是发现这个地址是一个无效地址,根本找不到那么就导致函数调用失败了;

        当我们的 编译阶段 通过 且 链接阶段 也通过后那么可执行程序就 成功生成了【其实可执行程序也是 elf 文件格式】,那么到这里我们的翻译环境就基本结束了; 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值