源文件的编译和可执行文件
开始
-
不同的代码(cpp,python,c,java)都是会编译称为相同的本地代码,本地代码就是类似于机器语言的母语,本地代码就是转换为机器语言的程序。
-
exe文件使用的就是本地代码,将每一个字节使用两位十六进制的数字表示,就可以得到本地代码的真实面目,这个过程叫做(Dump)
-
编译器的三大特点:
- 编程语言
- 生成的代码作用的CPU
- 哪种环境下运行的(运行环境=操作系统+硬件)
-
从源代码到可执行文件:
-
编译
-
gcc -o -w helloworld.cpp
上面的命令会生成一个.obj格式的文件,称为目标文件。目标文件不是可执行文件,因为没有执行链接。
-
-
链接:
-
链接的过程就是把多个目标文件结合形成一个可执行文件的过程,分为下面两种:
- 使用库文件:
- 静态链接库:库文件,格式为.lib,包含了一些已经写好的函数的比如标准库函数的目标文件,
- 动态链接库(导入库):仅仅包含了一些函数的文件夹信息和函数的处于哪个dll文件的信息,有的时候并不一定要使用导入库,只要能够在动态链接的过程中使用dll函数就行,注意动态链接并不是把目标文件结合的过程,而是类似于一种API的调用。
- 不使用库文件:
- 在执行链接的过程中要手动添加所需要的目标文件。
- 使用库文件:
-
启动:
- 就算不需要额外的函数或者API(Windows提供的程序应用接口),目标文件也一定要执行链接,原因在与可执行文件一定要包含一个启动的目标文件,这是编译器提供的,它包含了程序的起始位置相结合的内容。
-
-
执行:
- 可执行文件生成后如果需要运行会被加载到内存中,同时产生栈和堆:
- 栈:用于保存函数执行时临时的变量数据以及函数调用的所需要的空间的内存区域。这就很容易解释为什么需要把大内存的变量数据放到主函数外面,因为主函数里面所用到的所有内存都是在一个栈中。在执行处理数据的操作结束后,相应的内存会被清理掉。
- 堆:用于保存程序执行时所有数据和对象的内存领域。
- 堆和栈都是在程序执行的过程中申请分配的,如果没有及时清理掉内存,那么这个内存就会一直存在,处于使用状态,称作内存泄漏。这是十分麻烦的漏洞,很容易造成机器内存不足宕机。但是部分高级语言java,python,C#由于拥有高级的垃圾回收机制,会自动清理内存,防止内存泄漏。
- 再配置信息:
- 链接器会在可执行文件的开头配置程序执行的基址,所有的变量、函数的地址都是连续的分布在基址后面的内存中,注意程序里面的地址都是虚拟地址,并不是真正的物理地址,为了保证多个程序运行时不会发生内存冲突,多个程序的里面的虚拟地址可能相同,但是由于程序的基址不同,真正的物理地址是不一样的。
- 程序运行的内存=变量组的大小+函数组的大小+栈空间+堆空间+操作系统空间
- 可执行文件生成后如果需要运行会被加载到内存中,同时产生栈和堆:
-
-
一些名词:
- 分割编译:源文件分为多个文件,编译器编译的过程中就会产生多个目标文件,链接的时候多个目标文件就会结合到一个可执行文件中。但是,多文件的好处在于方便代码的管理。
- 叠加链接:有些函数一定不会同时执行,那么将他们交替加载到同一个内存地址中运行,就可以节省内存空间。
- dll文件的好处:
- 可以被多个程序同时使用,节省代码,节省内存空间。
- 修改的时候不需要重新链接并且导入到链接库中,只需要修改dll文件。