🚩write in front🚩
🔎大家好,我是謓泽,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎
🏅2021年度博客之星物联网与嵌入式开发TOP5~2021博客之星Top100~阿里云专家博主 & 星级博主~掘金⇿InfoQ~51CTO[创作者]~周榜373﹣总榜1055⇿全网访问量40w+🏅
🆔本文由 謓泽 原创 CSDN首发🙉如需转载还请通知⚠
📝个人主页-謓泽的博客_CSDN博客 📃
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝
📣系列专栏-【C】系列_謓泽的博客-CSDN博客🎓
✉️我们并非登上我们所选择的舞台,演出并非我们所选的剧本📩🌸程序的翻译环境和执行环境
在ANSIC的任何一种实现上都存在这两种不同的环境。
张三:ANSIC是什么东东,謓泽能不能说下ヾ(^▽^*)))。
什么张三同学学了这么久竟然连ANSIC是什么都忘记了,怎么回事(doge)
ANSIC实际上就是 美国国家标准协会(American National Standards Institute)协会制定的一个C语言的标准。任何C语言的编译器都在ANSIC的基础上扩充的。张三同学这个我们还是必须要了解的。
那么在上面说ANSIC的任何一种实现上都存在这两种不同的环境有⇣
- 🔥翻译环境🔥→在这个环境源代码被转换为可执行的机器指令。
在我们所使用的编译器像Vs所扮演的就是这个翻译环境。
- 🔥执行环境🔥→用于实际的执行代码当中。
这里的运行环境实际上就是执行环境。
🍀翻译环境
每个源程序也就是.c(可以是多个源程序)文件实际上都会经过编译器的处理,最后在各自生成一个目标文件.obj。
然后,这些目标文件一起就会生成一个叫做链接器的东西。在链接器进行链接的时候还会生成一个名为链接库的东西,把链接库连接到连接器当中去。最后在生成一个名为可执行文件(.exe)。
那么在这里介绍下什么是链接器和链接库如下↓
- 🎄链接器🎄→链接器(Linker)是一个程序,将一个或多个由编译器或汇编器生成的目标文件外加库链接为一个可执行文件。目标文件是包括机器码和链接器可用信息的程序模块。简单的讲,链接器的工作就是解析未定义的符号引用,将目标文件中的占位符替换为符号的地址。链接器还要完成程序中各目标文件的地址空间的组织,这可能涉及重定位工作。
- 🎄链接库🎄→一个函数当中有可能存在这链接库(library),然后这个链接库的信息就会一起存放在链接器当中去。这里的链接库就可能包含了这个函数的相关信息。
🌹翻译环境分支部分
在翻译环境中还存在几个步骤,先用一副图来表示如下↓
Ⅰ→预编译预处理:完成了对头文件(#include)的包含,#define定义的符号和宏的替换,也就是说会把宏定义数据赋值给对应变量的值,注释删除,因为注释是给我们打代码的人看的。它的指令是:gcc test.c - E (预处理后就停止)
Ⅱ→编译:把C语言的代码转换成汇编代码,那么肯定要做什么事情也就是我们所说的一个过程实际上有:语法分析、词法分析、语义分析、符号汇总(汇总全局变量的符号)。它的指令是:gcc test.i - S,生成 test.s
Ⅲ→汇编:对.s的文件进行汇编,把汇编代码转换成机器代码指令(二进制代码),还有进行了生成符号表(生成全局变量的符号)。它的指令是:gcc test.s -c,生成 test.o(test.obj)
- gcc 是编译器。
🌹链接器Linker
链接器工作的 ③ 个部分如下↓
- 将代码和数据模块象征性地放入内存。
- 决定数据和指令标签的地址。
- 修补内部和外部引用。
把多个目标文件和链接库来进行连接。
链接器使用每个目标模块中的重定位信息和符号表,来解析所有未定义标签。这种引用发生在分支指令、跳转指令和数据寻址处,所以这个程序的工作非常像一个编辑器:它寻找所有旧地址并用新地址取代它们:编辑是"链接编辑器"或链接器名字的简称。采用链接器的原因是修补代码比重新编译和汇编要快得多。可以说,通过这个地址就可以找到它所在的函数。
如果所有外部引用都解析完,链接器接着决定每个模块将要占用的内存位置。MlIPS在内存中为程序和数据分配空间的方式。因为文件是单独汇编的,所以汇编器不可能知道该模块的指令和数据相对于其他模块而言将会被放到哪里。当链接器将一个模块放到内存中的时候,所有绝对引用(absolute reference),即与寄存器无关的内存地址必须重定位以反映它的真实地址。
链接器产生一个可执行文件(executable file),它可以在一台计算机上运行。通常,这个文件与目标文件具有相同的格式,但是它不包含未解决的引用。具有部分链接的文件是可能的,如库程序,在目标文件中仍含有未解决的地址。
🍀运行环境
程序执行的过程如下⇣
①程序是必须载入的内存当中,在操作系统的环境中的环境中:一般都是由这个操作系统完成的。在独立的环境中,程序的载入都必须是手工进行安排,也可以是通过可执行代码置入只读的内存当中完成。
- 计算机当中所有的数据都是必须要放在内存当中的,不同类型的数据占用的字节数不一样。
- 常见的操作系统有很多种例如:Linux、Windows、macos 等
②程序的执行都是从 main() 函数当中开始的。
③开始执行程序代码,这个时候程序将会使用一个运行时候的堆栈(stack),存储函数的局部变量和返回的地址。当然程序也可以同时使用静态(stack)内存,存储于静态内存中的变量程序的整个执行过程一直会保存它们的值得。
栈
- 在执行函数的时候,函数内部局部变量的存储单元都是可以在栈上进行创建的,函数执行结束的时候这些存储单元会被自动的进行释放。栈区主要存放运行函数所分配的局部变量,函数的参数,返回数据,返回地址等。
堆
通常定义变量(或对象),编译器在编译时都可以根据该变量(或对象)的类型知道所需内存空间的大小,从而系统在适当的时候为他们分配确定的存储空间。这种内存分配称为静态存储分配;有些操作对象只在程序运行时才能确定,这样编译时就无法为他们预定存储空间,只能在程序运行时,系统根据运行时的要求进行内存分配,这种方法称为动态存储分配。所有动态存储分配都在堆区中进行。
堆当程序运行到需要一个动态分配的变量或对象时,必须向系统申请取得堆中的一块所需大小的存贮空间,用于存贮该变量或对象。当不再使用该变量或对象时,也就是它的生命结束时,要显式释放它所占用的存贮空间,这样系统就能对该堆空间进行再次分配,做到重复使用有限的资源。
④终止程序,正常终止main()函数,当然也可能是意外得终止。
👌总结-以上内容你学费了没~
★最后★ ⇢ 点赞👍 + 关注👋 + 收藏📑 == 学会✔