【C语言】 —— 编译和链接
【C语言】 —— 编译和链接
- 一、编译环境和运行环境
- 二、翻译环境
- 2.1、 预处理
- 2.2、 编译
- (1)词法分析
- (2)语法分析
- (3)语义分析
- 2.3、 汇编
- 2.4、链接
- 三、运行环境
一、编译环境和运行环境
平时我们说写 C语言 代码,写程序,不难发现:其实写出来的都是 t e s t test test. c c c、 t e s t test test. h h h 等源文件和头文件。我们直接打开他们,是可以直接看懂的,这也就说明了他们其实是文本文件
。但是计算机是看不懂他们的,计算机只能识别二进制指令
,无法对文件中的代码直接执行。这时就需要将 C语言 代码进行处理变成二进制的指令。
而将代码处理成二进制指令正是编译器需要做的事情。
在 ANCI C
的任何一种实现中,存在两个不同的环境:
- 翻译环境:在这个环境中源代码被转换成可执行的机器指令(二进制指令)
- 执行环境:它用于实际执行代码
二、翻译环境
那翻译环境是怎么将源代码转换为可执行的机器指令的呢?这里我们就得展开讲解一下翻译环境所做的事情
其实翻译环境是由编译和链接两大过程组成,而编译又可以分解成:预处理
(有些书也叫预编译
)、编译
、汇编
三个过程。
一个C语言的项目中可能由多个 . c c c 文件一起构建,那多个 . c c c 文件如何生成可执行程序呢?
- 多个 . c c c 文件
单独
经过编译器,编译处理生成对应的目标文件
。 - 在 W i n d o w s Windows Windows 环境下的目标文件的后缀是
.obj
,在 L i n u s Linus Linus环境下目标文件的后缀是.o
多个目标文件
和链接库
一起经过连接器处理生成最终的可执行程序
。- 链接库是指
运行时库
(它是支持程序运行的基本函数集合)、C语言库函数
或者第三方库
。
这里需提一下,我们所用的 V S 2022 VS2022 VS2022 是一种集成开发环境,包括:编辑器
、编译器
、链接器
、调试器
。而 c l cl cl. e x e exe exe 是其编译器, l i n k link link. e x e exe exe 是其链接器
我们可以在 L i n u s Linus Linus 服务器、 g c c gcc gcc 的环境下对链接和编译各个阶段进行观察
2.1、 预处理
在预处理阶段,源文件和头文件会被处理成 .i
为后缀的文件。
在 g c c gcc gcc 中,将 .c
文件处理成 .h
文件,命令如下:
gcc -E test.c -o test.i
预处理阶段主要处理那些源文件中 # 开始的编译指令。比如:# i n c l u d e include include,# d e f i n e define define,处理的规则如下:
- 将所有的 # d e f i n e define define 删除,展开所有的宏定义
- 处理所有的条件编译指令,如:
#if
、#ifdef
、#elif
、#else
、#endif
- 处理 # i n c l u d e include include 预编译指令,将包含的头文件的内容插入到该预编译指令的位置。这个过程是递归进行的,也就是说被包含的头文件也可能包含其他文件。
- 删除所有注释
- 添加行号和文件名标识,方便后续编译器生成调试信息等
- 或保留所有的 # p r a g m a pragma pragma 的编译器指令,编译器后续会使用
经过预处理后的 . i .i .i 文件不再包含宏定义
,因为宏已经被展开。并且包含的头文件
都被插入
到 .i 文件
中。所以我们无法知道宏定义或者头文件是否包含正确的时候,可以查看预处理后的 . i .i .i 文件来确认。
t e s t test test. c c c 文件:
t e s t test test. i i i 文件:
. i .i .i 文件只有输入指令生成后我们才能看到,正常情况下生成中间文件编译器用完就删掉了。
2.2、 编译
编译过程就是将预处理后的文件进行一系列的:词法分析、语法分析、语义分析及优化,生成相应的汇编代码文件
。
编译过程的命令如下:
gcc -S test.i -o test.s
编译过程最终会生成 .s
的文件,它里面放的是汇编代码。其实编译阶段整体上就是将 C 代码转换成汇编代码
t e s t . s test.s test.s 文件:
那编译过程具体是做了什么工作呢?
他们分别是:词法分析、语法分析、语义分析及优化
下面让我们一起简单了解
假设有下面的代码,在进行编译时会时编译器怎么做呢
array[index] = (index + 4) * (2 + 6);
(1)词法分析
将源代码程序输入扫描器
,扫描器的任务就是简单的进行词法分析
,把代码中的字符分割成一系列的记号(关键字、标识符、字面量、特殊字符等)。
上述代码进行词法分析后得到了 16 个记号:
记号 | 类型 | 记号 | 类型 |
---|---|---|---|
array | 标识符 | 4 | 数字 |
[ | 左方括号 | ) | 右圆括号 |
index | 标识符 | * | 乘号 |
] | 右方括号 | ( | 左圆括号 |
= | 赋值 | 2 | 数字 |
( | 左圆括号 | + | 加号 |
index | 标识符 | 6 | 数字 |
+ | 加号 | ) | 右圆括号 |
(2)语法分析
接下来则是语法分析
。语法分析器
会对扫描产生的记号进行语法分析,从而产生语法树
。这些语法树是以表达式为节点的树
(3)语义分析
由语义分析器
来完成语义分析,即对表达式的语法层面分析。编译器所能做的分析是语义的静态分析。静态语义分析通常包括声明和类型的匹配
。这个阶段会报告错误
的语法信息
2.3、 汇编
汇编是指通过汇编器将汇编代码转变成机器可执行的指令,每一个汇编语句几乎都对应一条机器指令。就是按照汇编指令和机器指令的对照表一一的进行翻译,也不做指令优化
汇编的命令如下:
gcc -c test.s -o test.o
经过汇编处理后文件即为目标文件( . o b j .obj .obj / . o .o .o)目标文件为二进制文件,无法通过文本编辑器打开
2.4、链接
链接是一个复杂的过程,链接的时候需要把一堆文件链接在一起才生成可执行程序。
链接的过程主要包括:地址和空间分配
,符号决议
和重定位
等这些步骤。
链接主要解决的是一个项目中多个文件、多模块之间互相调用的问题。
比如:现在有两个文件( t e s t . c test.c test.c 和 a d d . c add.c add.c)
t e s t . c test.c test.c 文件
#incldue<stdio.h>//声明外部函数
extern int Add(int x, int y);
//声明外部的全局变量
extern int g_val;int main()
{int a = 10;int b = 20;int c = Add(10, 20);printf("%d\n", c);return 0;
}
A d d . c Add.c Add.c 文件
int g_val = 2024;int Add(int x, int y)
{return x + y;
}
为什么在 Add.c
中定义的文件在 test.c
文件中声明一下就可以使用呢?
这里进行简单的了解
经过前面的学习,我们知道每一个源文件( . c .c .c)经过编译过程
后都会生成自己的目标文件
( . o .o .o / . o b j .obj .obj )
在编译的过程中,会对代码中的符号进行符号的汇总,并形成相应的符号表,符号表中会存储符号相对应的地址。在产生 t e s t . c test.c test.c 文件的符号表时,遇到只有声明而未定义的符号 Add
和 g_val
时,会暂时将其地址搁置。
到了编译过程,编译器会将多个目标文件链接在一起(目标文件的格式是一样的,并且是分段的形式),从而生成可执行程序(可执行策划给你续最终也是如目标文件一样的分段形式)
在合并过程中,符号表也需要合并成一份。合并后的符号表每个符号只能有一份,那 Add
和 g_val
符号自然用其有效地址的那一份,这样符号表就完成了合并。通过 A d d Add Add 的地址,就自然而然能找到 A d d Add Add 函数了。
而上述对地址的修正过程被叫做:重定位
前面我们非常简洁的讲解了一个 C 的程序是如何编译和链接,到最终生成可执行程序的过程,其实很多内部的细节无法展开讲解。
比如:目标文件的格式 e l f elf elf,链接底层实现中的空间与地址分配,符号解析和重定位等,如果你有兴趣,可以看 《程序员的自我修养》 一书来详细了解。
三、运行环境
运行环境实际上是非常复杂的,我们这里简单了解了解
- 程序必须载入内存中。在有操作系统的环境中:一般这个由
操作系统
完成。在独立的环境中,程序的载入必须由手动安排
(单片机烧板子),也可以通过可执行代码置入只读内存
来完成。 - 程序的执行便开始。接着便调用 m a i n main main 函数
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈(函数栈帧),存储函数的
局部变量
和返回地址
。程序同时也可以使用静态(static)内存
,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。 - 终止程序。正常终止 m a i n main main 函数;也有可能是意外终止。
好啦,本期关于编译和链接的知识就介绍到这里啦,希望本期博客能对你有所帮助。同时,如果有错误的地方请多多指正,让我们在C语言的学习路上一起进步!
相关文章:

【C语言】 —— 编译和链接
【C语言】 —— 编译和链接 一、编译环境和运行环境二、翻译环境2.1、 预处理2.2、 编译(1)词法分析(2)语法分析(3)语义分析 2.3、 汇编2.4、链接 三、运行环境 一、编译环境和运行环境 平时我们说写 C语言…...

DNS正向解析与反向解析实验
正向解析 安装bind软件 [rootlocalhost ~]# dnf install bind bind-utils -y修改主配置文件/etc/named.conf [rootlocalhost ~]# vim /etc/named.conf重启DNS服务(named) [rootlocalhost ~]# systemctl restart named编辑数据配置文件。在/var/named…...

机器学习简介--NLP(二)
机器学习简介 机器学习简介机器学习例子机器学习分类有监督学习有监督学习的应用 无监督学习 机器学习常见概念数据集k折交叉验证过拟合欠拟合评价指标 机器学习简介 机器学习例子 问题: 2,4,6,8,?&#…...

Winform中使用HttpClient实现调用http的post接口并设置传参content-type为application/json示例
场景 Winform中怎样使用HttpClient调用http的get和post接口并将接口返回json数据解析为实体类: Winform中怎样使用HttpClient调用http的get和post接口并将接口返回json数据解析为实体类_winform解析json-CSDN博客 上面使用HttpClient调用post接口时使用的HttpCon…...

【RAG探索第3讲】LlamaIndex的API调用与本地部署实战
原文链接:【RAG探索第3讲】LlamaIndex的API调用与本地部署实战 今天是2024年7月5日,星期五,天气晴,北京。 RAG的文章也看不少了,今天给大家带来一个llamaindex的实战。分为两个部分,调用ChatGLM的API来用l…...
C# —— 日期对象
DateTime 时间类 存储时间对象 可以获取当前时间 DateTime now DateTime.Now;// 获取当前时间 Console.WriteLine("年:" now.Year);//2023 Console.WriteLine("月:" now.Month);//9 Console.WriteLine("日:" now.Day);//12 Console.WriteLi…...

【MySQL04】【 redo 日志】
文章目录 一、前言二、redo 日志1. redo 日志格式2. Mini-Transaction2.1 以组的形式写入 redo 日志2.2 Mini-Transaction (MTR)概念 3. redo 日志写入过程3.1 redo 日志缓冲区3.3 redo 日志写入 log buffer 4. redo 日志文件4.1 redo 日志刷盘机制4.2 r…...

Android线性布局的概念与属性
线性布局(LinearLayout)是Android中最简单的布局方式,线性布局方式会使得所有在其内部的控件或子布局按一条水平或垂直的线排列。如图所示,图a是纵向线性布局示意图,图b是横向线性布局示意图。 a)纵向线性布局示意图 …...

java反射介绍
Java反射API允许你在运行时检查和修改程序的行为。这意味着你可以动态地创建对象、查看类的字段、方法和构造函数,甚至调用它们。这是一个强大的特性,但也应该谨慎使用,因为它可以破坏封装性。 以下是使用Java反射的一些常见用途:…...
Spring中@Transactional的实现和原理
这篇文章写的很详细了,引自脚本之家 Java中SpringBoot的Transactional原理_java_脚本之家...

华为仓颉可以取代 Java 吗?
大家好,我是君哥。 在最近的华为开发者大会上,华为亮相了仓颉编程语言,这是华为历经 5 年,投入大量研发成本沉淀的一门编程语言。 1 仓颉简介 按照官方报告,仓颉编程语言是一款面向全场景智能的新一代编程语言&#…...

性能测试相关理解(一)
根据学习全栈测试博主的课程做的笔记 一、说明 若未特别说明,涉及术语都是jmeter来说,线程数,就是jmeter线程组中的线程数 二、软件性能是什么 1、用户关注:响应时间 2、业务/产品关注:响应时间、支持多少并发数、…...

缓存-分布式锁-原理和基本使用
分布式锁原理和使用 自旋 public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {Boolean b redisTemplate.opsForValue().setIfAbsent(Lock, Lock, Duration.ofMinutes(1));if (!b) {int i 10;while (i > 0) {Object result redisTe…...
判断国内ip
php代码 //是否国内ip function isChinaIP($ip) {saveLog("---isChinaIP----------");$url "https://searchplugin.csdn.net/api/v1/ip/get?ip".$ip;// 发送HTTP请求$response file_get_contents($url);$utf8String mb_convert_encoding($response, &…...
linux修改内核实现禁止被ping(随手记)
概述 Linux默认允许被ping。其主要决定因素为: 内核参数防火墙(iptables/firewall) 以上的决定因素是与的关系,即需要均满足。 因此,修改linux禁被ping有以上两种方法可以实现。 修改内核文件使禁ping 1. 临时生…...

mac M1安装 VSCode
最近在学黑马程序员Java最新AI若依框架项目开发,里面前端用的是Visual Studio Code 所以我也就下载安装了一下,系统是M1芯片的,安装过程还是有点坑的写下来大家注意一下 1.在appstore中下载 2.在系统终端中输入 clang 显示如下图 那么在终端输…...

代码随想录算法训练营第二十七天 |56. 合并区间 738.单调递增的数字 968.监控二叉树 (可跳过)
56. 合并区间 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 示例 1: 输入:in…...

网络基础:IS-IS协议
IS-IS(Intermediate System to Intermediate System)是一种链路状态路由协议,最初由 ISO(International Organization for Standardization)为 CLNS(Connectionless Network Service)网络设计。…...

Java面试八股之如何提高MySQL的insert性能
如何提高MySQL的insert性能 提高MySQL的INSERT性能可以通过多种策略实现,以下是一些常见的优化技巧: 批量插入: 而不是逐条插入,可以使用单个INSERT语句插入多行数据。例如: INSERT INTO table_name (col1, col2) V…...

【密码学】什么是密码?什么是密码学?
一、密码的定义 根据《中华人民共和国密码法》对密码的定义如下: 密码是指采用特定变换的方法对信息等进行加密保护、安全认证的技术、产品和服务。 二、密码学的定义 密码学是研究编制密码和破译密码的技术科学。由定义可以知道密码学分为两个主要分支&#x…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...

【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...