编译与链接------《程序员的自我修养》
本篇整理于《程序员的自我修养》一书中编译与链接相关知识,整理的目的是为了更加深入的了解编译于链接的更多底层知识,面对程序运行时种种性能瓶颈我们束手无策。我们看到的是这些问题的现象,但是却很难看清本质,所有这些问题的本质就是软件运行背后的机理及支撑软件运行的各种平台和工具,如果能够深入了解这些机制,那么解决这些问题就能够游刃有余,收放自如了。
- 1.首言:
- 2.程序的翻译环境和执行环境
- 3.翻译环境中被隐藏的部分
- 3.1编译本身也分为几个阶段:
- ①预编译
- ②编译
- ③汇编
- ④链接
- ④.Ⅰ重定位
- ④.Ⅱ 符号引用---链接
- ④.Ⅲ 理解总结:
- 4.运行环境
- 5.总结:
1.首言:
对于平常的应用程序开发,我们很少需要关注编译和链接过程,因为通常的开发环境
都是流行的集成开发环境(IDE), 比如Visual Studio、Delphi 等。这样的IDE一般都将编
译和链接的过程.“一步完成,通常将这种编译和链接合并到一起的过程称为构建(Build)。
即使使用命令行来编译-一个源代码文件,简单的一句“gcchello.c"命令就包含了非常复
杂的过程。
IDE和编译器提供的默认配置、编译和链接参数对于大部分的应用程序开发而言已经
足够使用了。但是在这样的开发过程中,我们往往会被这些复杂的集成工具所提供的强大
功能所迷惑,很多系统软件的运行机制与机理被掩盖,其程序的很多莫名其妙的错误让我
们无所适从,面对程序运行时种种性能瓶颈我们束手无策。我们看到的是这些问题的现象,
但是却很难看清本质,所有这些问题的本质就是软件运行背后的机理及支撑软件运行的各
种平台和工具,如果能够深入了解这些机制,那么解决这些问题就能够游刃有余,收放自
如了。
2.程序的翻译环境和执行环境
在ANSI(标准) C的任何一种实现中,存在两个不同的环境。
- 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
- 第2种是执行环境,它用于实际执行代码。

3.翻译环境中被隐藏的部分
我们通常说源文件编译链接生成可执行程序:

- 1.组成一个程序的每个源文件通过编译过程分别转换成目标代码也叫目标文件(object code)。
- 2.每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
- 3.链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人 的程序库,将其需要的函数也链接到程序中。
3.1编译本身也分为几个阶段:

而这生成可执行程序就包括了这四个步骤:预编译,编译,汇编,链接。
①预编译
预编译阶段做了什么?
首先产生一个test.i文件
首先是源代码文件hello.c和相关的头文件,如stdio.h等被预编译器cpp预编译成一-个i
文件。对于C++程序来说,它的源代码文件的扩展名可能是.cpp或.cxx,头文件的扩展名可
能是.hpp,而预编译后的文件扩展名是.i。第一步 预编译的过程相当于如下命令(-E表示只
进行预编译):
$gcc -E hello.c -o hello. i
预编译过程主要处理那些源代码文件中的以“#”开始的预编译指令。
- 将所有的“#define"删除,并且展开所有的宏定义。
- 处理所有条件预编译指令,比如“#if”、 “#ifdef"、 “#elif" “#else"、 “#endif"。
- 处理“#include"预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这
个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。 - 删除所有的注释“//” 和“/ /”。
- 添加行号和文件名标识,比如#2“hello.c"2,以便于编译时编译器产生调试用的行号
- 信息及用于编译时产生编译错误或警告时能够显示行号。 保留所有的#pragma编译器指令,因为编译器须要使用它们。
②编译
编译阶段做什么?
生成一个test.s文件
-------把C语言代码翻译成汇编代码
会进行:
语法分析
词法分析——————更详细的请看《程序员的自我修养》一书
语义分析
重要:【符号汇总】–符号就是全局变量,函数
会将这些符号记录下来。
编译过程就是把预处理完的文件进行–系列词法分析、语法分析、语义分析及优化后生
产相应的汇编代码文件,这个过程往往是我们所说的整个程序构建的核心部分,也是最复杂的部分
③汇编
汇编阶段做什么?
生成一个test.o文件—目标文件
1.把汇编代码翻译成二进制指令存放到目标文件
2.形成符号表—会将符号给个地址,形成符号表
汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应-条机器
指令。所以汇编器的汇编过程相对于编译器来讲比较简单,它没有复杂的语法,也没有语义,
也不需要做指令优化,只是根据汇编指令和机器指令的对照表一-翻译就可以了 ,“汇编”
这个名字也来源于此。上面 的汇编过程我们可以调用汇编器as来完成:
$as he11o.s -0 he1l1o.o
④链接
链接通常是-一个让人比较费解的过程,为什么汇编器不直接输出可执行文件而是输出一
个目标文件呢?链接过程到底包含了什么内容?为什么要链接?
我们先看下 这个问题,我们创建3个.c文件

经过编译器这个个步骤以后,每个源代码终于被编译成了目标代码。但是test.o这个目标代码中有-一个问
题是:a和b的地址还没有确定。如果我们要把目标代码使用汇编器编译成真正能
够在机器_上执行的指令,那么a和b的地址应该从哪儿得到呢?如果a和b
定义在跟上面的源代码同一个编译单元里面,那么编译器可以为a和b分配空间,
确定它们的地址:那如果是定义在其他的程序模块呢?
这个看似简单的问题引出了我们一一个很大的话题:目标代码中有变量定义在其他模块,
该怎么办?事实上,定义其他模块的全局变量和函数在最终运行时的绝对地址都要在最终链
接的时候才能确定。所以现代的编译器可以将一个源代码文件编译成一个未链接的目标文
件,然后由链接器最终将这些目标文件链接起来形成可执行文件。让我们带着这个问题,走
进链接的世界。
④.Ⅰ重定位
由于在编译目标文件test.0的时候,编译器并不知道变量a,b的目标地址,所以编译器在没
法确定地址的情况下,将这条mov指令的目标地址置为0,等待链接器在将目标文件test.0与a.o和b.o
链接起来的时候再将其修正。我们假设test.o和a.o,b.o链接后,变量的地址确定下来为0x 1000,
0x1001,那么链接器将会把这个指令的目标地址部分修改成0x1000和0x1001这个地址修正的过程也被叫做
重定位(Relocation)
重定位所做的就是给程序中每个这样的绝对地址引用的位置“打补丁”,使它们指向正确的地址。
④.Ⅱ 符号引用—链接
函数访问须知道目标函数的地址,变量访问也须知道目标变量的地址,所以
这两种方式都可以归结为一种方式, 那就是模块间符号的引用。模块间依靠符号来通信类似
于拼图版,定义符号的模块多出一块区域,引用该符号的模块刚好少了那一块区域, 两者一
拼接刚好完美组合(见图2-7)。这个模块的拼接过程就:链接(Linking)。

简单来讲就是 在编译过程中每个模块会进行符号汇总,然后汇编过程会给每个符号定位个地址,这个地址要看该模块是否分配了,如果没有分配那就为空地址。
最后链接的目的就是将不同模块的符号的地址重定位,将符号的地址统一,使程序可以执行。
④.Ⅲ 理解总结:
编译和链接过程也并非想象中的那么复杂,它还是一个比较容易理解的概念。
比如我们在程序模块main.c中使用另外一个模块Add.c中的函数add()。我们在main.c模块中
每–处调用add的时候都必须确切知道add这个函数的地址,但是由于每个模块都是单独编
译的,在编译器编译main.c的时候它并不知道add函数的地址,所以它暂时把这些调用add
的指令的目标地址搁置,等待最后链接的时候由链接器去将这些指令的目标地址修正。如果
没有链接器,须要我们手工把每个调用add的指令进行修i正,则填入正确的add函数地址。
当Add.c模块被重新编译,add 函数的地址有可能改变时,那么我们在main.c中所有使用到
add的地址的指令将要全部重新调整。这些繁琐的工作将成为程序员的噩梦。
使用链接器,你可以直接引用其他模块的函数和全局变量而无须知道它们的地址,因为链接器在链接的时
候,会根据你所引用的符号add, 自动去相应的Add.c模块查找add的地址,然后将main.c
模块中所有引用到add的指令重新修n正,让它们的目标地址为真正的add函数的地址。这就
是静态链接的最基本的过程和作用。
总结:

4.运行环境
程序执行的过程:
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序
的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。 - 程序的执行便开始。接着便调用main函数。
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回
地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程
一直保留他们的值。 - 终止程序。正常终止main函数;也有可能是意外终止。
5.总结:
在本篇中, 我们首先回顾了从程序源代码到最终可执行文件的4个步骤:预编译、编
译、汇编、链接,分析了它们的作用及相互之间的联系,IDE集成开发工具和编译器默认的
命令通常将这些步骤合并成一一步,使得我们通常很少关注这些步骤。
我们还总结了上面这4个步骤中的主要部分,即编译步骤。介绍了编译器将C程序源代码转变成汇编代码的若干个步骤:词法分析、语法分析、语义分析、目标代码生成。
最后我们介绍了:重定位、符号、目标文件等概念
相关文章:
编译与链接------《程序员的自我修养》
本篇整理于《程序员的自我修养》一书中编译与链接相关知识,整理的目的是为了更加深入的了解编译于链接的更多底层知识,面对程序运行时种种性能瓶颈我们束手无策。我们看到的是这些问题的现象,但是却很难看清本质,所有这些问题的本质就是软件运…...
5分钟搞懂 强缓存与协商缓存
Ⅰ、http缓存 HTTP 缓存策略 分为 > 「强制缓存」 和 「协商缓存」 为什么需要 HTTP 缓存 呢 ? 👇 直接使用缓存速度 >> 远比重新请求快 缓存对象有那些呢 ?👇 「图片」 「JS文件」 「CSS文件」 等等 文章目录Ⅰ、http缓存Ⅱ…...
Ts笔记第一天
文章目录安装 ts运行环境 nodeTS类型数字 、字符串 和布尔类型字面量any 和unknown类型断言void和neverobjectArraytuple 元组enum 枚举安装 ts运行环境 node node-v看版本号 2. 安装ts -g全局安装 npm i -g typescript // 这里全局安装 -s安装无法使用tsc 创建一个01.ts文…...
Android 12 Activity启动流程
Android 12 Activity启动过程 参考文献: startActivity启动过程分析 Activity启动流程(Android 12) 概述 Activity启动发起后,是通过Binder最终交由system进程中的AMS来完成。 一、启动流程 frameworks/base/core/java/android/app/Activity.java f…...
VCS®/VCSi™User Guide
VCS是一种高性能、高容量的Verilog模拟器,它将先进的高级抽象验证技术集成到一个开放的本地平台中。VCS是一个编译代码模拟器。它使您能够分析、编译和模拟Verilog、SystemVerilog、OpenVera和SystemC设计描述。它还为您提供了一组模拟和调试功能,以验证…...
MongoDB简介及SpringBoot整合
一、概述MongoDB中的记录是一个文档,它是一个数据结构组成 字段和值对。MongoDB文档类似于JSON。对象。字段的值可能包括其他文档、数组、 和文档数组:数据库(Database):和关系型数据库一样,每个数据库中有…...
读书思考:步步惊心的《技术陷阱》
《技术陷阱》这本书450页,43万字之巨,信息量密密麻麻,采集的资料极其丰富,复习了一遍大停滞、大分流、大平衡、大逆转时代,并展望未来。看完了有很多想法,随手写了下来,希望不是蹭热点。&#x…...
求你了,不要再在对外接口中使用枚举类型了!
最近,我们的线上环境出现了一个问题,线上代码在执行过程中抛出了一个IllegalArgumentException,分析堆栈后,发现最根本的的异常是以下内容: java.lang.IllegalArgumentException: No enum constant com.a.b.f.m.a.c.A…...
Java开发学习(四十六)----MyBatisPlus新增语句之id生成策略控制及其简化配置
在前面有一篇博客:Java开发学习(四十一)----MyBatisPlus标准数据层(增删查改分页)开发,我们在新增的时候留了一个问题,就是新增成功后,主键ID是一个很长串的内容。 我们更想要的是按照数据库表字段进行自增…...
章鱼哥听歌
uboot环境变量 以下所有的命令,都在串口工具进行执行 ubifsmount- mount UBIFS volume ubifsumount- unmount UBIFS volume ums - Use the UMS [USB Mass Storage] usb - USB sub-system usbboot - boot from USB device version - print monit…...
软件测试电商项目实战(写进简历没问题)
前言 说实话,在找项目的过程中,我下载过(甚至付费下载过)N多个项目、联系过很多项目的作者,但是绝大部分项目,在我看来,并不适合你拿来练习,它们或多或少都存在着“问题”ÿ…...
算法导论—分治法思想、动态规划思想、贪心思想
算法导论—分治法思想、动态规划思想、贪心思想分治法的思想:动态规划:贪心算法:贪心算法求解问题的条件:设计贪心算法的步骤:分治法的思想: 将原问题分解为几个规模较小但类似于原问题的子问题࿰…...
Spring-Data-Jpa实现继承实体类
写在前面:从2018年底开始学习SpringBoot,也用SpringBoot写过一些项目。现在对学习Springboot的一些知识总结记录一下。如果你也在学习SpringBoot,可以关注我,一起学习,一起进步。 相关文章: 【Springboot系…...
多线程环境下的伪共享
今天和大家聊一聊伪共享 1.什么是伪共享? 缓存一致性协议在计算机中针对的最小单元:缓存行,每个缓存行的大小是64字节,一串连续的64字节数据都会存储到缓存行中。 假设数据A和数据B在同一缓存行中,CPU1修改了数据A&am…...
【Taylor and Francis】1/2区云计算、物联网、机器学习类,SCIEEI双检,审稿友好
机器学习类 【期刊简介】IF:6.5-7.0,JCR1/2区,中科院3区 【检索情况】SCIE&EI双检 【参考周期】2-3个月左右录用 【征稿领域】面向制造业云计算物联网应用的机器学习方法 【截稿日期】10篇版面 毕业必看-快刊 计算机科学类…...
CleanMyMac X4.12新版本下载及功能介绍
CleanMyMac X2023最新版终于迎来了又4.12,重新设计了 UI 元素,华丽的现代化风格显露无余。如今的CleanMyMac,早已不是单纯的系统清理工具。在逐渐融入系统优化、软件管理、文件管理等功能后,逐渐趋近于macOS的系统管家,…...
大数据技术架构(组件)26——Spark:Shuffle
2.1.6、Shuffle2.1.6.0 Shuffle Read And WriteMR框架中涉及到一个重要的流程就是shuffle,由于shuffle涉及到磁盘IO和网络IO,所以shuffle的性能直接影响着整个作业的性能。Spark其本质也是一种MR框架,所以也有自己的shuffle实现。但是和MR中的shuffle流程…...
关于Zebec生态的改进提案,即将上线的 Nautilus 链
概括 在最初作为 Solana 原生应用程序推出一年后,Zebec 团队已经能够通过在 BNB和NEAR区块链上成功部署来扩大其产品的范围。 凭借继续向尽可能多的公司/协议/基金提供薪资工具和基础设施的雄心勃勃的计划,我们决定采用最终将使 Zebec生态系统及其核心…...
Python数据可视化(三)(pyecharts)
分享一些python-pyecharts作图小技巧,用于展示汇报。 一、特点 任何元素皆可配置pyecharts只支持python原生的数据类型,包括int,float,str,bool,dict,list动态展示,炫酷的效果,给人视觉冲击力 # 安装 pip install pyecharts fr…...
【Redis面试指南】
Redis面试指南 Redis是一个开源的、基于内存的、高性能的键值对存储系统,它可以用于存储非常大量的数据,并且可以在短时间内获取数据。Redis的性能被广泛用于Web应用程序的缓存层,以提高应用程序的性能和可用性。Redis的面试是一个比较复杂的…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
