C++:Article:链接器(二):符号决议
链接器
- 1. C++源文件都有些什么
- 1.1 . 目标文件里有什么
- 2. 符号表 Symbol table
- 2.1. 符号表的位置
- 2.2. 符号的决议
- 2.3. 符号决议过程
- 3. 实例说明
- 3.1. 意外出现
- 3.2 总结排查
在上篇文章中,我们介绍了 链接器基本概念,我们知道所有的应用程序否是连接器将所需要的一个个简单的目标文件汇集起来形成的。
比如:我们在 list 中实现了一种特定的链表数据结构,其他模块需要使用这种链表,这就是模块直接的依赖。
-
确保目标文件能找到符号定义 (Symbol Resolution) 符号决议
🚀 链接器的其中一项重要任务就是 确保提供链接器进行链接的目标文件集合之间依赖是成立的(也就是说不会出现在被依赖的模块中链接器找不到需要依赖的接口)。 -
可执行程序的生成
🚀 链接器会首先将程序每个模块当中目标文件集合链接成库,然后在将各个库进行链接最终形成可执行程序。 -
重定位
🚀 在完成 符号决议和生成可执行文件之后,链接器需要对可执行文件进行重定位。
下面我们围绕上面三个部分来详细的讲解下每一个过程。
1. C++源文件都有些什么
一个典型的C++源文件中,该文件中的变量可以划分为两类。
- 全局变量:只要程序没结束运行,全局变量都可以随时使用。(注意:静态的全局变量生命周期也等于程序的运行周期,只是这种全局变量只能在所 被定义的文件当中使用,对其它文件不可见)。
- 局部变量:局部变量的生命周期和全局变量的生命周期不同,局部变量只能在相应的函数内部使用,当函数调用完毕后,局部变量也不能使用 。
// 1. 定义未初始化的全局变量
int x_global_uninit;
// 2. 定义初始化的全局变量
int x_global_init = 1;
// 3. 定义未初始化的全局私有变量,该变量只能在当前文件中使用
static int y_global_unint;
// 4. 定义未初始化的全局私有变量,该变量只能在当前文件中使用
static int y_global_init = 2;
// 5. 声明全局变量,但是该变量在其他文件中定义
extern int z_global;
// 6. 声明函数,该函数在其他文件中定义
int fn_a(int x, int y);
// 7. 函数定义,因为使用static 修饰,该函数只能在当前文件中使用
static int fn_b(int x)
{return x+1;
}
// 8. 函数定义,该函数可以被其他文件使用
int fn_c(int x_local)
{// 9. 未初始化的局部变量int y_local_unint;// 10. 已初始化的局部变量int y_local_init = 3;// 11. 全局变量,局部变量以及函数的使用x_global_uninit = fn_a(x_local,x_global_init);y_local_uninit = fn_a(x_local,y_local_init);y_local_uninit += fn_b(z_global);return (y_global_uninit+ y_local_uninit);
}
1.1 . 目标文件里有什么
编译器的任务就是把人类可以理解的代码 转换成机器可以执行的机器指令,源文件编译后形成对应的目标文件本质上可以分成两部分
- 代码部分:计算机可以执行的机器指令,也就是源文件中定义的所有函数,比如上图中:fn_a() fn_b() 等
- 数据部分:源文件中定义的全局变量,如果是已经初始化后的全局变量,该全局变量的值也存在于数据部分。
2. 符号表 Symbol table
编译器在编译过程中遇到外部定义的全局变量或函数时,只要能在当前文件中找到其声明即可,编译器就会认为是正确的。
寻找变量的定义就被留给了 链接器,链接器的一项重要任务就是要确定所使用的变量要有其唯一的定义。虽然这项工作留给了链接器,但是为了让链接器工作的轻松一点,编译器还是多做了一点工作,这部分工作就是 符号表(Symbol table)。 那么符号表保存的是什么了 ?
🚀🚀🚀
- 该目标文件中引用的全局变量以及函数
- 该目标文件中定义的全局变量以及函数
如 标题2所知,编译器在编译过程中,每次遇到一个全局变量或者函数名都会在符号表中添加一项,最终编译器会统计出如下所示的一张读好表
2.1. 符号表的位置
符号表被编译器很贴心的放在目标文件中,因此一个目标 文件可以理解为下图所示的三段
2.2. 符号的决议
在上一节符号表中,我们知道符号表给链接器提供了两种信息。
- 一个是当前目标文件可以提供给其他目标文件使用的符号。
- 另一个是其他目标文件需要提供给当前目标文件使用的符号。
2.3. 符号决议过程
如下图所示,假设链接器需要链接下面三个文件。链接器会一次扫描每一个给定的目标文件,同时链接器还维护了两个集合,一个是已定义符号集合D,另一个是未定义的集合U,下面是链接器进程符号决议的过程:
- 对于当前目标文件,查找其符号表,并将已定义的符号并添加到已定义符号集合D中。
- 对于当前目标文件,查找其符号表,将每一个当前目标文件引用的符号与已定义符号集合D进行对比,如果该符号不在集合D中则将其添加到未定义符号集合U中。
- 当所有文件都扫描完成,如果未定义符号集合U不为空,则说明当前输入的目标文件中有未定义符号错误,链接器就会报错,整个编译过程就会终止。
3. 实例说明
// 伪代码
// math.h
#include<iostream>
int add(int a, int b);// math.cpp
#include<iostream>
int add(int a, int b)
{return a+b;
}
// main.cpp
#include<iostream>
#include "math.h"
int main()
{int sum = add(1,2);std::cout << sum << std::endl;return 0;
}
链接过程如下:
编译器在 链接 main.o 和 math.o 文件时。
- matth.o 目标文件有add() 函数符号,首先会在当前文件查找定义,结果当前文件就存在add() 函数定义,所以直接将符号add 添加到已定义集合D中
- main.o 目标文件也有add() 函数符号,首先会在当前文件中查找定义,结果当前文件不存在add() 函数定义,然后在集合D中查找是否有定义,结果找到了符号的定义
- 当完成 main.o 和 math.o 两个目标文件链接后,编译终止,生成可执行文件。
3.1. 意外出现
假设你不小心将 math.cpp 中的add函数注释了,但是main.cpp 仍然引用了add() 函数符号,当你在编译的时候,就会报很经典的
undefined reference to add(int, int) 错误。
现在我们来分析下产生这个错误的原因。
- 编译器发现你写的代码 main.o 中引用了外部定义定义的函数(通过检查目标文件 main.o 中的符号表得到的信息),所以链接器开始寻址这个add()符号到底是在哪里定义的。
- 链接器先去目标文件 main.o 的符号表中查找,没有找到 add() 符号的定义。
- 转而链接器去其他目标文件符号表中查找,通用没有找到add() 函数符号的定义
- 链接器在查找了所有目标文件的符号表后都没找到add() 函数符号,因此链接器停止工作并报出 undefined reference to add(int, int)
3.2 总结排查
所以根据前面几节的介绍讲解,你已经很清楚的知道链接器符号决议整个过程,当出现 未定义符号错误时,你可以进行如下排查
- main.cpp 中对add函数的函数名是否书写正确
- 链接命令中是否包含了 math.o ,如果没有,那么需要添加上该目标文件
- 如果链接命令没有问题,查看 math.cpp 中关于 add函数的定义是否存在问题
- 如果是C和C++的混合编程,确保相应的位置添加 extern C
一般情况下,经过这几个步骤的排查,基本能解决上述问题
相关文章:

C++:Article:链接器(二):符号决议
链接器 1. C源文件都有些什么1.1 . 目标文件里有什么 2. 符号表 Symbol table2.1. 符号表的位置2.2. 符号的决议2.3. 符号决议过程 3. 实例说明3.1. 意外出现3.2 总结排查 在上篇文章中,我们介绍了 链接器基本概念,我们知道所有的应用程序否是连接器将所…...
期权价格上下限与期权平价关系
目录 1. 期权的基本概念 2. 期权的上下限 3. 期权的平价关系 1. 期权的基本概念 期权:是一种选择权,期权买方向卖方支付一定数额的 期权费 后,可获得在 一定时间(到期日)内以 一定价格(执行价格&#x…...

QT中TCP的学习
文章目录 qt中TCP的实现 qt中TCP的实现 学习视频 QT中可以通过TCP协议让服务器和客户端之间行通信。服务器和客户端的具体流程 下方的信号都是系统提供的,我们只需要写相应的槽函数 A、服务器: 创建QTcpServer对象启动服务器(监听&…...
编译选项与常用环境变量
一、编译选项与常用环境变量 1、命令选项 -D 相当于就是定义,-D 可以理解为告诉cmake 后边我要定义一些参数,每定义一个就在前边加上-D就可以了,示例: #!/bin/shcmake -DTEST_DEBUGON . cmake --build .2、编译选项 下面列出来的…...

【SpringBoot2】SpringBoot开发实用篇
SpringBoot开发实用篇 KF-1.热部署 什么是热部署?简单说就是你程序改了,现在要重新启动服务器,嫌麻烦?不用重启,服务器会自己悄悄的把更新后的程序给重新加载一遍,这就是热部署。 热部署的功能是如…...
接口自动化测试框架搭建全部过程
思想: 1、基本目录的搭建 report:静态输出目录(报告或者日志) data:静态输入目录(可以存放Excel数据,被读取的一些数据) utils:实用方法层(这里存放的是项目的公共方法,一般拿到别…...
SQL学习(十)--DML_多表查询(针对数据表记录的join查询、子查询的操作)
目录 1. 多表查询 -- 内连接查询 1.1 显示内连接 1.2 隐式内连接 2. 多表连接 -- 外连接查询...
Docker容器部署
Docker容器部署 为什么使用Docker什么是Docker类比用途 Docker基于Windows集成IDEA在window上安装docker设置Docker配置IDEA连接Docker测试启动SpringBoot应用测试 Docker基于Linux集成IDEA连接宿主机redis服务连接Docker中redis服务 为什么使用Docker 在和前端联调的过程中&a…...

26岁转行网络安全,成功上岸安全开发!
前言 我是去年 9 月 22 日才正式学习网络安全的,之前在国营单位工作了 4 年,在长沙一个月工资只有 5000 块,而且看不到任何晋升的希望,如果想要往上走,那背后就一定要有关系才行。 而且国营单位的气氛是你干的多了&a…...

涨点技巧: 谷歌强势推出优化器Lion,引入到Yolov8,内存更小、效率更高,秒杀Adam(W)
1.Lion优化器介绍 论文:https://arxiv.org/abs/2302.06675 代码:automl/lion at master google/automl GitHub 1.1 简单、内存高效、运行速度更快 1)与 AdamW 和各种自适应优化器需要同时保存一阶和二阶矩相比,Lion 只需要动量,将额外的内存占用减半; 2)由于 Lion…...

5年测试经验,自动化都不会?月薪11K都难拿....
我接触了太多测试同行,由于多数同行之前一直做手工测试,现在很迫切希望做自动化测试,其中不乏工作5年以上的同行。 我从事软件自动化测试已经近十年,接触过底层服务端、API 、Web、APP、H5 等等,对自动化算是比较了解…...

低代码平台名声臭,用起来却真香——60%开发者不敢承认
群体盲从意识会淹没个体的理性,个体一旦将自己归入该群体,其原本独立的理性就会被群体的无知疯狂所淹没。——《乌合之众》 不知道从什么时候开始,“低代码不行”的论调充斥着整个互联网圈子,csdn、掘金、知乎、B站、脉脉……到处…...
PHP 的代码简洁之道(Clean Code PHP)
介绍 Robert C.Martin’s 的 软件工程师准则 Clean Code 同样适用于 PHP。它并不是一个编码风格指南,它指导我们用 PHP 写出具有可读性,可复用性且可分解的代码。 并非所有的准则都必须严格遵守,甚至一些已经成为普遍的约定。这仅仅作为指导方…...
delphi在两个窗口间用消息通讯
用SendMessage在窗口间通讯: 发送方 var HWD: THandle; str1,str2:string; sData: TCopyDataStruct; begin HWD:FindWindow(nil,pchar(aaaaaa)); // Integer(pchar(self.Edit2.Text)) str2:我来了中玉人; str1:我来了中玉人; sData.cbDa…...

如何高效提高倾斜摄影三维模型顶层合并的技术方法分析
如何高效提高倾斜摄影三维模型顶层合并的技术方法分析 1、倾斜摄影三维模型顶层合并 1.1倾斜摄影三维模型是一种基于倾斜摄影技术,通过多个角度拍摄同一区域的影像,利用计算机图像处理和三维重建技术生成的三维地理信息数据。由于一个大区域可能需要多块…...

【科普】PCB为什么常用50Ω阻抗?6大原因
在PCB设计中,阻抗通常是指传输线的特性阻抗,这是电磁波在导线中传输时的特性阻抗,与导线的几何形状、介质材料和导线周围环境等因素有关。 对于一般的高速数字信号传输和RF电路,50Ω是一个常用的阻抗值。 为什么是50Ω?…...

Linux嵌入式uboot使用tftp网络启动加载zImage、设备树
文章目录 一、前言二、Linux U-boot 相关命令(1)help 命令(2)printenv 命令(3)setenv 函数(4)saveenv 函数 三、tftp启动linux内核步骤(1)进入u-boot模式&…...

使用Serv-U搭建FTP服务器并公网访问【内网穿透】
文章目录 1. 前言2. 本地FTP搭建2.1 Serv-U下载和安装2.2 Serv-U共享网页测试2.3 Cpolar下载和安装 3. 本地FTP发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 科技日益发展的今天,移动电子设备似乎成了我们生活的主角,智能…...

后端大厂面试总结大全六
目录: 1、Transactional注解控制事务有哪些不生效的场景2、MySQL的优化 1、Transactional注解控制事务有哪些不生效的场景 数据库引擎不支持事务数据源没有配置事务管理器没有被spring管理方法不是public的同一个类中方法调用,导致Transactional失效 举…...
2023五一数学建模A题B题C题思路模型代码
占个位置吧,开始在本帖实时更新五一数学建模赛题思路代码,文章末尾获取! 持续为更新参考思路 赛题思路 会持续进行思路模型分析,下自行获取。 A题思路: (比赛开始后第一时间更新) B题思路…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...