.C文件如何转化成可执行文件

我们都知道,当写出一个程序的时候按下运行程序就会跑起来,然后我们就可以根据程序的共功能开始自己的操作,那么一个程序究竟是怎么样从一个.c的文件变成一个可以执行的文件,今天在这里和大家探讨一下。

一:程序的翻译环节

程序在开始运行的时候首先会经过翻译,也就是把我们的源程序翻译成可执行的额机器指令(二进制指令)。这里我们可以用VS2019进行观察一下。当我们创建好这个文件并且已经写好代码的时候,对应路径下面就会生成一个.C的文件,当我们运行之后,文件中就会生成一个.obj位后缀的文件,在windo环境下面,这个就是我们的机器可识别的指令在经过编译链接最后就产生了一个后缀位.exe的可执行文件。

 如果一个工程里面有多个源文件呢,其实也是同样的,多个程序都会先经各自经过编译器生成一个后缀位.obj的文件,然后在和链接库(比如printf一类的库函数)一起经过链接器的处理最后生成可执行文件。

1:预处理(预编译)

程序在经过编译的时候同样会经过三个阶段

首先给大家讲一下什么预处理,同样我们可以测试一下,这里我们用linux上的gcc来经行观察。

 

 

 这是我们随意写的一个文件,然后我们让gcc来编译它只让他停在预处理阶段。

就会发现生成了一个test.i的文件

 

 然后我们点进去可以看一下这个后缀为.i的文件里面有些什么

 我们可以看到前面包含了相当多的东西,最低端才是我们写的代码。仔细观察的话会发现前面有很多的一些不是我们写的东西也在里面其实这些就是我们的头文件的内容,定义的宏M也直接变成了10,写的注释也没有了。经过这里我们就可以说,在预处理阶段所做的其实就是

包含头文件

删除注释

宏的替换和删除这三个步骤了。

2:编译

那么经过第一个阶段的预处理程序来到了第二个阶段,编译环节。在这里程序将会把我们的源程序翻译成汇编代码,我们同样可以看一下

这里先用gcc编译test.i的文件,让他生成一个.s的汇编文件。

 我们进去在看一下就会看到全是一些汇编代码,如果有兴趣的话可以尝试解读一下。

 那么在编译环节我们的代码又经历了什么呢。

语法分析:分析你的程序是否有错,语法是否按照要求

词法分析:当代码一行一行开始读的时候遇到int一类的关键字会被检查出来

语义分析:分析这行代码是什么需求

符号汇总。当我们在上去看汇编代码的时候会发现里面好像有几个我们认识的东西

 

 这不就是我们定义的全局变量函数和main吗。到这里就能理解原来符号汇总就是把这些全局和函数一类的东西划分到一起来形成符号表。

3:汇编

当编译阶段完成就来到了编译环节的最后一个阶段,汇编。我们同样可以观察一下,在汇编阶段干了什么。同样我们让gcc去编译test.c的文件让他停在汇编环节。

当我们用gcc去编译完成之后可以看到这里生成了一个.o的文件,这个文件就是obje在windo环节下就是obj的文件。同样我们进到里面去看一下

 这就是我们机器可以识别的二进制当然我们是看不懂的。

4:链接

最后来到我们的链接环节,链接分为两个步骤

合并段表

这里我在拿两端代码来举例

 在前面说到每个文件都会经过编译生成目标文件而这些目标文件都是elf的一种文件格式,在而elf会把这些文件划分成一个一个的段,里面存放的都是一些功能不同的数据。

 

 而合并段表的时候就是把这两个表合并在一起。

 

 

符号表的合并和重定位

 每个文件在经过编译生成目标文件里面也会有一些符号,先看第一段代码里面的符号有ADD,X,main三个而每个符号都有一个地址。

第二段代码里面有一个ADD 

前面我们说到每文件都会经过编译器生成一个后缀为.o的文件,但是最后多个文件都会经过编译器生成一个文件。

 那我们就要把这些符号的地址合并到一起

当合并在一起的时候里面有两个ADD的地址就产生了冲突。其实不然,在合并的时候我们的Test文件里面的ADD只是用于一个声明,没有实际功能,它的地址也就是无效的,也就是说,在合并的时候只会保留一个有效的ADD地址也就是0X500的地址,而0X100的地址就会被舍弃掉。

当说清楚之后我们可以去测试一下,还是用上面两段代码,但是,如果我们在调用ADD函数的时候函数名不是ADD而是aDD的话就会出现这样一个错误

 我们可以用上面的逻辑尝试分析一下,这两个文件在生成各自的目标文件之后也会有各自的符号表

 当着两个符号表开始链接到一起的时候就会产生这样一个符号表

 当我ADD拿着这个地址去找这个函数的时候发现没有这个函数,那这个时候就会产生这样一个错误说无法识别的外部符号ADD。这样我们就知道了,以后只要出现这样的错误就一定是链接期间出现的错误。

二:执行环节

 

程序执行是从内存中开始的,这个是由不同的操作系统决定是怎么样执行的。计算机中的所有程序都是放在内存中的,不同的字节占用不同的内存,但所有程序都是从mian函数开始的。

在执行程序的时候,内存中又被分为栈,堆。栈上是一些比如临时变量,函数一类,在程序执行结束的时候会自动释放空间。而堆则是由程序员自己向内存申请的一块空间,使用之后需要自行释放,如果不释放就会一直增加空间损耗。比如你在一家公司上班,写了一个程序,这个程序是需要7*24小时的工作,但是你在申请了空间之后忘记释放了,那么就会24小时不间断的损耗空间,当内存耗干的时候程序就会停止工作,但是你重启一次又会发现程序可以正常运行了这就是不释放空间的后果。

 

 如果想更深层次了解可以去看看《程序员的自我修养》

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要将.so文件合并成可执行文件,首先需要将.so文件与对应的.o文件链接起来。.so文件是动态链接库文件,其中包含了编译过的目标代码。而.o文件是编译后生成的目标文件,包含了编译过的源代码。 通过使用链接器,可以将.so文件与对应的.o文件一起链接成一个可执行文件。链接器的作用是将各个目标文件的代码和数据段合并成一个可执行文件。链接器会解析符号引用,并将符号引用与符号定义进行匹配,以确保代码的正确执行。 在链接的过程中,需要确保.so文件和.o文件之间的依赖关系被正确处理。这包括解析符号引用、处理动态链接库的加载和链接等步骤。 总结起来,要将.so文件合并成可执行文件,需要使用链接器将.so文件与对应的.o文件链接起来,并处理它们之间的依赖关系。这样就可以生成一个包含了所有必要代码和数据的可执行文件。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [合并EXE和DLL文件.使产生一个可执行文件](https://download.csdn.net/download/ythtwzj/2855415)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [关于c ++:.o / .a / .so文件中到底是什么?](https://blog.csdn.net/weixin_39644377/article/details/117206423)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值