【C语言】程序运行过程:预处理/编译/汇编/链接

本文详细介绍了C语言程序的编译过程,包括预编译、编译、汇编和链接四个阶段。预编译主要处理头文件、注释和宏定义;编译阶段将C代码转换为汇编代码;汇编阶段则将汇编代码转化为二进制目标文件;链接阶段合并符号表并解决不同源文件间的依赖。此外,还提到了运行环境中的程序加载和执行流程。
摘要由CSDN通过智能技术生成


哈喽啊,盆友们。一起来看看 C语言中编译预处理的内容吧!😜

1.程序运行的几个阶段

众所不周知,C语言的程序运行分为几个阶段。

咱们可以看看下面这个图,简单了解一二👇

image-20220302105951684

细分开来,编译还分为3个小阶段:预编译(预处理)、编译、汇编

image-20220302110221434

这三个阶段又分别做了什么事情呢?这就需要我们用linux下的gcc编译器来验证了

1.1预编译

现在我们编写了一个这样的代码

image-20220302110948614

image-20220302111004125

在运行窗口中输入以下指令,进行预编译操作,得到test.i文件

gcc -E test.c -o test.i

image-20220302111042754

打开该文件,滑倒最底部,查看它与源代码的不同

image-20220302111224560

可以发现以下几点:

  • 头文件消失:实际上被展开了
  • 注释被删除
  • define定义的符号被替换

这就是预编译阶段做的3件事,实际上都是一些文本操作,并没有运行该代码

1.2编译

输入以下指令,生成test.s文件

gcc -S test.i -o test.s

image-20220302111725318

打开该文件,发现我们好像看不太懂它里面写了些什么

image-20220302111848369

实际上,movsub都是汇编语言,这一步就是把C语言代码转变成了汇编代码。 进行了以下几步操作:

  1. 语法分析
  2. 词法分析
  3. 语义分析
  4. 符号汇总

image-20220302112256166

这一部分内容可以阅读《程序员的自我修养》这本书,记录一下

1.3汇编

gcc -c test.s -o test.o

image-20220302112625672

这个指令会生成一个test.o目标文件

  • Linux:目标文件后缀为.o
  • Windows:目标文件后缀为.obj

.o目标文件和可执行成句的文件格式elf

readelf工具可以解析elf格式的文件

用文本编辑器打开这个文件,可以看到里面的东西都是乱码

image-20220302112532139

实际上这个文件里面存放的是二进制内容。

汇编操作就是将汇编代码转换成二进制指令

其中有非常重要的一部:形成符号表

1.4符号表和链接

这个代码中其实包含了两个源文件:test.cAdd.c

image-20220302113122577

里面出现了两个符号,main和Add

编译器会先对每个源文件创立一个符号表,为它们添加一个类似地址的参数。在链接阶段的时候,相同符号的地址参数会被设置为一样的

这里编译会查看Add函数的定义在哪里

如main函数中只是extern,并没有Add的具体实现,最后链接之时,Add符号的地址会被设置为Add.c中该符号的地址

这个操作又叫:符号表的合并和重定位

链接阶段还会执行另外一个操作:合并段表

gcc test.o add.o -o test
//生成可执行程序test

image-20220302113722693

使用./TEST操作执行该文件,可以看到程序成功输出了Add后的答案

image-20220302113738246


2.运行环境

程序执行的过程中:

  1. 程序先载入内存。在操作系统中,这个操作由系统完成;在独立环境中,程序的载入必须手动完成,也可以是通过可执行代码置入只读内存来完成
  2. 程序执行开始。调用main函数
  3. 开始执行代码。这时将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时使用静态内存,存储于静态内存中的变量在程序的整个执行过程中保留它们的值
  4. 终止程序。正常终止main函数,也可能是意外终止

结语

本篇博客中,我们认识了解了程序运行的几个阶段。这里面还有很多更深层次的问题待我去探究。

下篇博客:预处理详解

点个赞再走呗,谢谢!

  • 69
    点赞
  • 124
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
当然可以,下面是一段简单的 C 语言代码,以及编译流程的详细解释: 代码如下,保存为 `hello.c` 文件: ```c #include <stdio.h> int main() { printf("Hello, World!\n"); return 0; } ``` 1. 预处理(Preprocessing): 执行以下命令进行预处理: ``` gcc -E hello.c -o hello.i ``` 预处理器会将头文件 `stdio.h` 中的内容插入到源代码中,生成一个预处理后的文件 `hello.i`。 2. 编译(Compilation): 执行以下命令进行编译: ``` gcc -S hello.i -o hello.s ``` 编译器会将预处理后的文件 `hello.i` 转化为汇编代码 `hello.s`。其中,汇编代码包含了源代码的每个函数和变量的调用和访问信息,但是还没有被转化为机器码。 3. 汇编(Assembly): 执行以下命令进行汇编: ``` gcc -c hello.s -o hello.o ``` 汇编器会将汇编代码 `hello.s` 转化为可重定位目标文件 `hello.o`。可重定位目标文件包含了汇编代码转化而来的机器码,但是还没有被链接成可执行文件。 4. 链接(Linking): 执行以下命令进行链接: ``` gcc hello.o -o hello ``` 链接器会将可重定位目标文件 `hello.o` 链接成一个可执行文件 `hello`。在链接过程中,链接器会将未定义的符号与其他目标文件中的定义符号匹配起来,以生成最终的可执行文件。 最终,我们可以通过以下命令来运行 `hello` 程序: ``` ./hello ``` 输出结果为: ``` Hello, World! ``` 这个过程中还会涉及到符号表管理、错误处理、代码优化等多个环节,以确保生成的代码正确、高效、可维护。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慕雪华年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值