C语言可执行程序到底怎样生成?

目录

程序的翻译环境

NO1.VS编译器工具

NO2.VS链接器工具

NO3.链接库是什么?

编译

预处理

编译

汇编

链接

程序的执行环境


C语言的程序到底是怎样生成的呢?又怎样去执行呢?我们来探索。本篇是讲解编译环境。

ANSI C(标准C语言) 的任何一种实现中,存在两个不同的环境。第一种 是翻译环境,在这个环境中源代码.c被转换为可执行的机器指令.exe,第二种 是执行环境,它用于实际执行代码。

test.c----->test.i------->test.s----->test.obj----->test.exe

程序的翻译环境

程序的翻译环境同时又分为两个阶段 编译 和 链接

  • 组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
  • 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
  • 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。
  • 在windows环境下,生成相应的目标文件test.obj
  • 在Linux环境下,生成的目标文件是test.o
  • test.c源文件---------->test.obj目标文件+链接库------------>test.exe可执行程序

理解图:

以我们的VS编译器为例子。编译和链接依赖的是什么工具呢?

NO1.VS编译器工具

  • 那在我们的VS2022集成开发环境下,VS依赖就是cl.exe这个工具去实现编译这项功能。
  • 在windows环境下,生成相应的目标文件test.obj
  • 在Linux环境下,生成的目标文件是test.o

NO2.VS链接器工具

  • VS2022集成开发环境下,VS依赖就是cl.exe这个工具去实现链接这项功能。
  • 在不同的编译环境下,标准库中的实现肯定是有差异的。

NO3.链接库是什么?

cs 复制代码
#include<stdio.h>
int main()
{
	printf("hehe\n");
	return 0;
}
  • 例如 像上面的#include<stdio.> printf库函数 我们就直接使用,是因为放在了链接库中直接供给我们使用。标准库中 提供的 现成的库函数 都是以链接库的形式给我们提供的。

编译

其实编译本身也分为几个阶段:预编译(预处理) 编译 汇编 这三个阶段。我们接下来分别探索一下这三个阶段分别干了什么事情。我们整个过程都在 :Linux环境下,使用gcc编译器去验证整个过程。test.c------->test.i--------->test.s----->test.o(test.obj)

具体要用到的指令:

  1. 预处理 选项 gcc -E test.c -o test.i 预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。
  2. 编译 选项 gcc -S test.c 编译完成之后就停下来,结果保存在test.s中。
  3. 汇编 gcc -c test.c 汇编完成之后就停下来,结果保存在test.o中。

预处理


我们想要展示一个预处理的效果,那我们希望预处理之后这个代码就不会往后继续执行了。这里我们要用到一个gcc的指令:-E test.c / test.c -E预处理之后test.c代码不会往后继续执行
在我们VS验证之后我们发现我们预处理的文件名为test.i 所以这里在Linux环境下,我们使用gcc指令:-o test.i (把test.c预处理完之后的文件命名为test.i,如果没有指定就直接打印在屏幕上了)

在观察了test.c和test.i两个文件差异我们发现预处理对源代码做了一些文本操作处理的。

  • 注释的替换(删除)。注释被替换成一个空格
  • 头文件的包含#include< >
  • #define符号的替换
  • #include和#define这种都是 预处理指令。所有的预处理指令都在预处理阶段处理的。

在我们的下篇博客我们也会详细去讲到预处理这个阶段的知识点。

注释的删除

头文件的包含

编译


来到我们的编译阶段,我们想要展示一个编译的效果,那我们希望编译之后这个代码就不会往后继续执行了。这里我们要用到一个gcc的指令:-S test.i / test.i -S编译之后test.i代码不会往后继续执行
同时我们也可以使用gcc指令:-o test.s (把test.i编译完之后的文件命名为test.s,如果没有指定也会生成test.s)

我们发现test.s里面放置的都是汇编代码。 其实编译的过程就是:把C语言代码翻译成了汇编代码。这个过程是非常非常复杂的。简单来说,编译这个过程包含:

  • 语法分析
  • 词法分析
  • 语义分析
  • 符号汇总

汇编


到这里,汇编语言依旧不能被我们计算机读懂,汇编语言必须经过汇编器转化成二进制指令,才能被计算机读懂。

我们想要展示一个汇编的效果,那我们希望汇编之后这个代码就不会往后继续执行了。这里我们要用到一个gcc的指令:-c test.s / test.s -c汇编之后test.s代码不会往后执行
同时我们也可以使用gcc指令:-o test.o(test.obj) (把test.s汇编完之后的文件命名为test.o,如果没有指定也会生成test.o)

我们发现test.o里面放置的都是二进制信息代码。 其实汇编的过程就是:把汇编代码翻译成二进制指令(目标文件)。到此为止,计算机看得懂了。

  • 生成符号表

链接

现在我们的目标文件test.obj 在gcc指令下:gcc test.o(test.obj) -o test.c(test.exe) (把test.o链接完之后的文件命名为test.c,如果没有指定也会生成test.c(其实就是test.exe可执行程序)

  • 链接目标文件obj和链接库生成可执行程序(二进制程序)
  • 合并段表
  • 合并符号表和符号表的重新定位

程序的执行环境

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

【推荐书籍】《程序员的自我修养》

【VIM学习资料】

简明 Vim 练级攻略 | 酷 壳 - CoolShell

【给程序员的VIM速查卡】

https://coolshell.cn/articles/5479.html

✔✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎指正!

希望大家都有好好学习,好好敲代码。好好生活哦

代码------→【gitee:唐棣棣 (TSQXG) - Gitee.com

联系------→【邮箱:2784139418@qq.com】

相关推荐
Want59510 分钟前
C/C++跳动的爱心
c语言·开发语言·c++
水瓶丫头站住10 分钟前
Qt中QDockWidget的使用方式
开发语言·qt
kongba00711 分钟前
Cursor提示词模板,开发GD32,C语言开发GD32 ARM单片机编程规范提示词 大厂风格代码规范
c语言·arm开发·单片机
laimaxgg16 分钟前
Qt常用控件之数字显示控件QLCDNumber
开发语言·c++·qt·qt5·qt6.3
蓝天扶光20 分钟前
c++贪心系列
开发语言·c++
奔跑吧邓邓子1 小时前
【Python爬虫(44)】分布式爬虫:筑牢安全防线,守护数据之旅
开发语言·分布式·爬虫·python·安全
LaoZhangGong1231 小时前
STM32的“Unique device ID“能否修改?
c语言·经验分享·stm32·单片机·嵌入式硬件
C#Thread1 小时前
C#上位机--流程控制(IF语句)
开发语言·javascript·ecmascript
牵牛老人2 小时前
Qt开发中出现中文乱码问题深度解析与解决方案
开发语言·qt
大脑经常闹风暴@小猿2 小时前
1.1 go环境搭建及基本使用
开发语言·后端·golang