【C】编译与链接
在本文章里面,我们讲会讲解C语言程序是如何从我们写的代码一步步变成计算机可以执行的二进制指令,并最终执行的。C语言程序运行主要包括两大步骤 -- 编译和链接,接下来我们就来一一讲解。
目录
1 翻译环境和运行环境
2 翻译环境
1) 预处理(预编译)
2) 编译
(1) 词法分析
(2) 语法分析
(3) 语义分析
3) 汇编
4) 链接
1 翻译环境和运行环境
在ANSI C(标准C)的任何一种实现里面,都会包含两个不同的环境:
1) 翻译环境:会将源代码转变为可执行的二进制的机器指令。
2) 运行环境:用于执行实际的代码。
两个环境之间的关系可以用图片来描述一下:
用语言简单改过一下就是,各种源文件通过翻译环境中的编译和链接两步,会将我们写的C代码转变成机器能够执行的二进制指令,然后生成可执行程序(.exe为后缀的文件),在运行环境中就可以运行了。
所以可执行程序中存放的都是二进制指令,如果直接用记事本打开会看到是一堆乱码:
2 翻译环境
翻译环境主要由编译和链接两步组成,而编译又由预处理(预编译)、编译、汇编三个过程,所以翻译环境也可以说成是由预处理、编译、汇编、链接四小部分组成。
一个C语言的项目一般会由多个源文件共同构成,每一个源文件会先经过编译器(在vs中为cl.exe)生成对应的目标文件(Windows环境下后缀为.obj,Linux环境下后缀为.o),然后在经过链接器(vs中为link.exe)把各个目标文件和链接库(运行时库(支持程序运行的基本函数的集合)和第三方库)一起链接为可执行程序。
1) 预处理(预编译)
在预处理阶段,源文件和头文件会被处理成为后缀为 .i 的文件。
预处理阶段主要会处理一些以 # 开头的预处理指令,具体规则如下:
1 | 将所有的 #define 删除,并展开所有的宏定义 |
2 | 处理所有的预编译指令,如 #if 、#endif、#ifdef、#elif、#else 等等 |
3 | 处理 #include 预编译指令,将包含的头文件的内容插入到预编译指令的位置 |
4 | 删除所有注释 |
5 | 添加行号和文件名标识,方便后续编译器生成调试信息等 |
6 | 保留所有的 #pragma 的编译器指令,编译器后续使用 |
第6条我们曾在结构体内存对齐中曾经使用过,如:
//修改默认对齐数为1
#pragma pack(1)//还原为默认对齐数
#pragma pack()
2) 编译
编译的主要作用将C语言代码转变为汇编代码,会对文件进行一系列的:词法分析,语法分析,语义分析及优化,生成相应的汇编代码文件。
如以下的这个代码:
arr[index] = (index + 5) * (3 * 8);
(1) 词法分析
将原代码程序输入扫描器,然后由扫描器进行词法分析,将代码中的字符分割成一系列的记号(关键词,标识符,特殊字符等)。上述代码会在扫描器中会分割成以下记号:
(2) 语法分析
经过词法分析之后,语法分析器会对产生的记号进行语法分析,产生语法树(以表达式为结点的树):
(3) 语义分析
接下来就是由语义分析器来完成语义分析,即对表达式进行语法层面分析。编译器的分析是语义分析的静态分析,通常包括声明和类型的匹配,类型转换等等。在这个阶段会报告错误的语法信息。
语义标识后的语法树如图所示:
3) 汇编
经过了预处理和编译之后,就到了汇编阶段,在该阶段会用汇编器将汇编代码转变为机器执行的指令,也就是二进制指令,不做指令优化。
经过了以上三个阶段,就生成了目标文件。
4) 链接
链接会将一堆文件链接在一起,生成一个可执行程序。
链接过程主要包括:地址和空间分配,符号决议和重定位等。其实链接解决的是一个项目中多文件、多模块之间的相互调用问题。
比如:
//add.c
int Add(int x, int y)
{return x + y;
}//test.c
#include<stdio.h>//extern声明外部函数
extern int Add(int, int);int main()
{int a = Add(3, 5);printf("%d\n", a);return 0;
}
在add.c源文件中有一个Add函数。而在test.c源文件中,用extern关键字声明了外部函数并在main函数中使用了Add函数。
其实在链接的过程中,每一个源文件中分别会对函数和全局变量来生成一个符号表,这个符号表里面存储了函数和全局变量的地址,对于定义了的函数和全局变量会在符号表中存储真正的地址,而对于文件中没有定义只是声明的函数和全局变量的地址会先在符号表中存储一个无效的地址,然后链接的时候,会将符号表合并,在其他模块里面寻找真正定义过相同函数和全局变量的地址,来修正文件中引用到该函数的地址,若符号表中仍存在无效地址,咋会报链接错误。
比如在以上例子中,假设test.c 生成的符号表为:
Add函数 | 0x00000000(无效地址) |
main函数 | 0x1187f3c0 |
Add函数 | 0x12f2d7c4 |
在链接时,会将两个符号表合并生成一个新的符号表:
Add函数 | 0x12f2d7c4 |
main函数 | 0x1187f3c0 |
再比如以下这个例子:
//add.c
int add(int x, int y)
{return x + y;
}//test.c
#include<stdio.h>extern int Add(int x, int y);int main()
{int ret = Add(3, 5);return 0;
}
Add函数 | 0x00000000(无效地址) |
main函数 | 0x1187f3c0 |
add函数 | 0x12f2d7c4 |
Add函数 | 0x00000000(无效地址) |
main函数 | 0x1187f3c0 |
add函数 | 0x12f2d7c4 |
合成的符号表中出现了无效地址,所以运行时会报链接错误:
相关文章:

【C】编译与链接
在本文章里面,我们讲会讲解C语言程序是如何从我们写的代码一步步变成计算机可以执行的二进制指令,并最终执行的。C语言程序运行主要包括两大步骤 -- 编译和链接,接下来我们就来一一讲解。 目录 1 翻译环境和运行环境 2 翻译环境 1&#…...

Github上传项目
写在前面: 本次博客仅仅是个人学习记录,不具备教学作用。内容整理来自网络,太多了,所以就不放来源了。 在github页面的准备: 输入标题。 往下滑,创建 创建后会跳出下面的页面 进入home就可以看到我们刚…...
webrtc之rtc::ArrayView<const uint8_t>
rtc::ArrayView<const uint8_t> 是 WebRTC(或其他基于 rtc 命名空间的库)中常见的一个类型,它通常用于表示一块 只读的内存区域,该内存区域由一系列 uint8_t 类型(无符号 8 位整数)元素组成。 1. rt…...

Zemax 序列模式下的扩束器
扩束器结构原理 扩束器用于增加准直光束(例如激光束)的直径,同时保持其准直。它通常用于激光光学和其他需要修改光束大小或发散度的应用。 在典型的扩束器中,输入光束是准直激光器,或光束进入第一个光学元件。当光束开…...

Flink系统知识讲解之:如何识别反压的源头
Flink系统知识之:如何识别反压的源头 什么是反压 Ufuk Celebi 在一篇古老但仍然准确的文章中对此做了很好的解释。如果您不熟悉这个概念,强烈推荐您阅读这篇文章。如果想更深入、更低层次地了解该主题以及 Flink 网络协议栈的工作原理,这里有…...

RK3568平台(USB篇)禁用USB端口
一.linux中怎样查看usb的端口号 在USB口插入U盘: [ 198.141319][ T106] usb 3-1.3: new SuperSpeed Gen 1 USB device number 5 using xhci-hcd [ 198.161695][ T106] usb 3-1.3: New USB device found, idVendor=0781, idProduct=5591, bcdDevice= 1.00 [ 198.161721]…...
洛谷 P3000 [USACO10DEC] Cow Calisthenics G
思路 题目要求断若干条边后形成的连通块中,最大的直径最小,很明显的二分。关键就在于如何写 c h e c k check check 函数了。 可以用 d f s dfs dfs 来判断要断哪条边。 一、 d [ u ] d[u] d[u] 定义 设 d [ u ] d[u] d[u] 为从 u u u 出发到子树…...

Web渗透测试之XSS跨站脚本攻击 盲打 详解
目录 XSS盲打 什么是盲打: 盲打主要目的 XSS盲打 什么是盲打: 发现某个页面有xss漏洞 但是注入后没看到效果 而是在其它页面进行xss显示的效果 这种就叫盲打. 我注册了一个网站的用户 注册页面存在xss漏洞跳转到首页 看不到注册信息的输出 当管理员打开页面查看什么用户…...
经典编程题:服务器广播
题目描述: 服务器连接方式包括直接相连,间接连接。A 和 B 直接连接,B 和 C 直接连接,则 A 和 C 间接连接。直接连接和间接连接都可以发送广播。 给出一个 N*N 数组,代表 N 个服务器,matrix[i][j]1…...

【网络协议】静态路由详解
网络中的路由器通过以下两种方式之一发现远程网络: 静态配置路由动态路由协议 在本文,我们将学习关于静态路由的各种概念,例如如何配置静态路由、路由表如何进行决策、路由接口等相关知识。 文章目录 引言直连网络静态路由路由表原则原则1原…...

朝天椒USB服务器在银泰证券虚拟化超融合场景的应用案例
在数字化浪潮席卷金融行业的今天,银泰证券作为业内知名的金融机构,始终致力于提升业务运营效率与数据安全性。面对虚拟化超融合场景下各种认证U盾的管理挑战,银泰证券选择了朝天椒USB服务器作为其解决方案,成功实现了U盾在虚拟机中…...

.NET framework、Core和Standard都是什么?
对于这些概念一直没有深入去理解,以至于经过.net这几年的发展进化,概念越来越多,越来越梳理不容易理解了。内心深处存在思想上的懒惰,以为自己专注于Unity开发就好,这些并不属于核心范畴,所以对这些概念总是…...

FairGuard游戏安全2024年度报告
导 读:2024年,国内游戏市场实际销售收入3257.83亿元,同比增长7.53%,游戏用户规模6.74亿人,同比增长0.94%,市场收入与用户规模双双实现突破,迎来了历史新高点。但游戏黑灰产规模也在迅速扩大&…...
JetBrains IDEs和Visual Studio Code的对比
JetBrains IDEs和Visual Studio Code的对比 JetBrains IDEs是捷克JetBrains公司开发的一系列集成开发环境(IDE)。以下是具体介绍:IntelliJ IDEA是JetBrains 公司的一款产品 主要产品 IntelliJ IDEA:一款功能强大且广泛应用的Java集成开发环境,有开源免费的社区版和商业收…...

文件剪切走:深度解析与高效恢复策略
一、文件剪切走现象解读 在计算机的日常使用中,“文件剪切走”这一术语形象地描述了文件在移动过程中意外丢失的现象。当用户尝试将文件从一个位置“剪切”并粘贴到另一个位置时,如果操作不当或系统出现异常,可能会导致文件在源位置消失&…...

Win32汇编学习笔记09.SEH和反调试
Win32汇编学习笔记09.SEH和反调试-C/C基础-断点社区-专业的老牌游戏安全技术交流社区 - BpSend.net SEH - structed exception handler 结构化异常处理 跟筛选一样都是用来处理异常的,但不同的是 筛选器是整个进程最终处理异常的函数,但无法做到比较精细的去处理异常(例如处理…...
[人工智能]CSDN创作助手体验
一、什么是智能体 智能体是一种能够感知环境、学习、推理和行动的实体。它可以是一个计算机程序、机器人或其他类似的系统。智能体的目标是通过与环境的交互来实现特定的任务或目标。 智能体通常由以下几个组件组成: 感知器:感知器是智能体与环境之间的…...

vue3中el-table实现多表头并表格合并行或列
1、el-table中添加事件 :span-method"genderSpanCity" <el-table :span-method"genderSpanCity":data"data.tableData":fit"true" table-layout"fixed" header-align"center" stripestyle"width:100%;he…...

HTML+CSS+JS制作中国传统节日主题网站(内附源码,含5个页面)
一、作品介绍 HTMLCSSJS制作一个中国传统节日主题网站,包含首页、节日介绍页、民俗文化页、节日活动页、联系我们页等5个静态页面。其中每个页面都包含一个导航栏、一个主要区域和一个底部区域。 二、页面结构 1. 顶部横幅区 包含传统中国风格的网站标题中国传统…...

时空笔记:CBEngine(微观交通模拟引擎)
CBEngine 是一个微观交通模拟引擎,可以支持城市规模的道路网络交通模拟。CBEngine 能够快速模拟拥有数千个交叉路口和数十万辆车辆的道路网络交通。 以下内容基本翻译自CBEngine — CBLab 1.0.0 documentation 1 模拟演示 1.0 模拟演示结构 config.cfg 定义了 roa…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...