目录
引入
编译
预编译
编译
汇编
链接
选项总结
记忆方法
运行环境
引入
博主认为学习本章内容,能够认识在代码跑的时候的'过程'。
首先,粗略笼统的认识程序运行过程的框架图。
编译
其次,再进行细化,细化编译的过程,深入了解编译三个步骤,包含预编译/预处理、编译、汇编。了解链接与编译的交接,自然而然就豁然开朗了。
博主的想法是先告诉大家上述的三个步骤大致是干嘛的,然后由浅入深的理解。
预编译
预编译也可以叫预处理,具体样例:头文件的展开,#define定义的标识符常量转换(标识符转换成值),注释的删除等,第一步骤是文本操作。
向下我就要用Linux跟大家演示了,因为VS是集成开发环境,编译的过程细化是看不到的。
大致步骤:创建文件->编辑文件->编译三步骤
这里的编译选项-E就涉及到编译步骤的选项了,-E是对文件进行预处理,-o 是output把预处理好的文件重定向到指定文件中,后缀设置成.i也是依照预处理后的文件属性规定。
然后我进入到test.i一探究竟!
可以看到注释消失了(给代码玩家们看的,编译器又不看),标识符常量也被更换成了具体的值,而包含的头文件进行了展开,证明了预处理操作是进行文本操作。
编译
编译就是将预处理后的语言转换成汇编代码,汇编代码会经过语法分析、词法分析、语义分析、符号汇总这四个过程,使用-S选项进行编译,生成.s后缀的文件。
test.s内容
除了符号汇总大家都能理解,符号汇总汇总的是全局符号。
汇编
汇编是将汇编码转换成二进制码,并且在这个汇编过程中还会形成符号表,符号表存放的是符号名和地址。-c选项是进行汇编,形成以.o为后缀的目标(object/obj)文件。
test.o的实际内容:
形成符号表是要在链接时使用。
每一种文件的都有自己的组织方式,这里就要提到Linux下.o为后缀的文件,这种二进制内容经过汇编步骤的文件,是以elf格式组织的。
链接
把一个项目的所有文件(test.c、add.c)以及链接库合到一起,生成.exe为后缀的可执行程序。行动:1、合并段表;2、符号表的合并和重定位
实际上.exe文件在Linux环境下也是elf格式,elf格式就是分类分割不同的数据,相同数据在一起。
所以合并段表就是将不同文件的相同类型的数据合并到一起,合并方式还是类似于归类。
符号表的合并,字面意思将所有的符号表合并在一起,符号表的重定位也是因为向下方图第二个add的地址是无效地址(为了做演示,依据编译器不同而不同),所以就重定位为0x100为add的地址。
当然如果第一个add函数没写,第二个add函数当然是一个无效的地址了,因为找不到add,所以这里编译器就会报链接错误。
名字写错,例如大小写错误,也是一样的
选项总结:
1.预处理 选项 gcc -E test.c -o test.i
预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。
2. 编译 选项 gcc -S test.c
编译完成之后就停下来,结果保存在test.s中。
3. 汇编 gcc -c test.c
汇编完成之后就停下来,结果保存在test.o中。
记忆方法:
选项是键盘上的ESC键,生成的文件后缀iso,相似于ios操作系统,但是只要记住目标文件(.o)是在自后就行了,s相当于一个滑坡,一个弯道,过了这个弯道就是目标.o。
运行环境
程序执行的过程:
1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序
的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
2. 程序的执行便开始。接着便调用main函数。
3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
4. 终止程序。正常终止main函数;也有可能是意外终止。
书籍推荐:《程序员的自我修养》
谢谢大家阅读本章内容,欢迎大家在评论区留言讨论、补充、指错。让本章知识点内容更加的充实拓宽,谢谢!