当前位置: 首页 > news >正文

C++:Article:链接器(二):符号决议

链接器

  • 1. C++源文件都有些什么
    • 1.1 . 目标文件里有什么
  • 2. 符号表 Symbol table
    • 2.1. 符号表的位置
    • 2.2. 符号的决议
    • 2.3. 符号决议过程
  • 3. 实例说明
    • 3.1. 意外出现
    • 3.2 总结排查

在上篇文章中,我们介绍了 链接器基本概念,我们知道所有的应用程序否是连接器将所需要的一个个简单的目标文件汇集起来形成的。
比如:我们在 list 中实现了一种特定的链表数据结构,其他模块需要使用这种链表,这就是模块直接的依赖。

  1. 确保目标文件能找到符号定义 (Symbol Resolution) 符号决议
    🚀 链接器的其中一项重要任务就是 确保提供链接器进行链接的目标文件集合之间依赖是成立的(也就是说不会出现在被依赖的模块中链接器找不到需要依赖的接口)。

  2. 可执行程序的生成
    🚀 链接器会首先将程序每个模块当中目标文件集合链接成库,然后在将各个库进行链接最终形成可执行程序。

  3. 重定位
    🚀 在完成 符号决议和生成可执行文件之后,链接器需要对可执行文件进行重定位。

下面我们围绕上面三个部分来详细的讲解下每一个过程。

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 . 目标文件里有什么

编译器的任务就是把人类可以理解的代码 转换成机器可以执行的机器指令,源文件编译后形成对应的目标文件本质上可以分成两部分

  1. 代码部分:计算机可以执行的机器指令,也就是源文件中定义的所有函数,比如上图中:fn_a() fn_b() 等
  2. 数据部分:源文件中定义的全局变量,如果是已经初始化后的全局变量,该全局变量的值也存在于数据部分。

2. 符号表 Symbol table

编译器在编译过程中遇到外部定义的全局变量或函数时,只要能在当前文件中找到其声明即可,编译器就会认为是正确的。
寻找变量的定义就被留给了 链接器,链接器的一项重要任务就是要确定所使用的变量要有其唯一的定义。虽然这项工作留给了链接器,但是为了让链接器工作的轻松一点,编译器还是多做了一点工作,这部分工作就是 符号表(Symbol table)。 那么符号表保存的是什么了 ?

🚀🚀🚀

  1. 该目标文件中引用的全局变量以及函数
  2. 该目标文件中定义的全局变量以及函数
    如 标题2所知,编译器在编译过程中,每次遇到一个全局变量或者函数名都会在符号表中添加一项,最终编译器会统计出如下所示的一张读好表

在这里插入图片描述

2.1. 符号表的位置

符号表被编译器很贴心的放在目标文件中,因此一个目标 文件可以理解为下图所示的三段
在这里插入图片描述

2.2. 符号的决议

在上一节符号表中,我们知道符号表给链接器提供了两种信息。

  1. 一个是当前目标文件可以提供给其他目标文件使用的符号。
  2. 另一个是其他目标文件需要提供给当前目标文件使用的符号。

2.3. 符号决议过程

如下图所示,假设链接器需要链接下面三个文件。链接器会一次扫描每一个给定的目标文件,同时链接器还维护了两个集合,一个是已定义符号集合D,另一个是未定义的集合U,下面是链接器进程符号决议的过程:
在这里插入图片描述

  1. 对于当前目标文件,查找其符号表,并将已定义的符号并添加到已定义符号集合D中。
  2. 对于当前目标文件,查找其符号表,将每一个当前目标文件引用的符号与已定义符号集合D进行对比,如果该符号不在集合D中则将其添加到未定义符号集合U中。
  3. 当所有文件都扫描完成,如果未定义符号集合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 文件时。

  1. matth.o 目标文件有add() 函数符号,首先会在当前文件查找定义,结果当前文件就存在add() 函数定义,所以直接将符号add 添加到已定义集合D中
  2. main.o 目标文件也有add() 函数符号,首先会在当前文件中查找定义,结果当前文件不存在add() 函数定义,然后在集合D中查找是否有定义,结果找到了符号的定义
  3. 当完成 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 总结排查

所以根据前面几节的介绍讲解,你已经很清楚的知道链接器符号决议整个过程,当出现 未定义符号错误时,你可以进行如下排查

  1. main.cpp 中对add函数的函数名是否书写正确
  2. 链接命令中是否包含了 math.o ,如果没有,那么需要添加上该目标文件
  3. 如果链接命令没有问题,查看 math.cpp 中关于 add函数的定义是否存在问题
  4. 如果是C和C++的混合编程,确保相应的位置添加 extern C
    一般情况下,经过这几个步骤的排查,基本能解决上述问题

相关文章:

C++:Article:链接器(二):符号决议

链接器 1. C源文件都有些什么1.1 . 目标文件里有什么 2. 符号表 Symbol table2.1. 符号表的位置2.2. 符号的决议2.3. 符号决议过程 3. 实例说明3.1. 意外出现3.2 总结排查 在上篇文章中&#xff0c;我们介绍了 链接器基本概念&#xff0c;我们知道所有的应用程序否是连接器将所…...

期权价格上下限与期权平价关系

目录 1. 期权的基本概念 2. 期权的上下限 3. 期权的平价关系 1. 期权的基本概念 期权&#xff1a;是一种选择权&#xff0c;期权买方向卖方支付一定数额的 期权费 后&#xff0c;可获得在 一定时间&#xff08;到期日&#xff09;内以 一定价格&#xff08;执行价格&#x…...

QT中TCP的学习

文章目录 qt中TCP的实现 qt中TCP的实现 学习视频 QT中可以通过TCP协议让服务器和客户端之间行通信。服务器和客户端的具体流程 下方的信号都是系统提供的&#xff0c;我们只需要写相应的槽函数 A、服务器&#xff1a; 创建QTcpServer对象启动服务器&#xff08;监听&…...

编译选项与常用环境变量

一、编译选项与常用环境变量 1、命令选项 -D 相当于就是定义&#xff0c;-D 可以理解为告诉cmake 后边我要定义一些参数&#xff0c;每定义一个就在前边加上-D就可以了&#xff0c;示例&#xff1a; #!/bin/shcmake -DTEST_DEBUGON . cmake --build .2、编译选项 下面列出来的…...

【SpringBoot2】SpringBoot开发实用篇

SpringBoot开发实用篇 KF-1.热部署 ​ 什么是热部署&#xff1f;简单说就是你程序改了&#xff0c;现在要重新启动服务器&#xff0c;嫌麻烦&#xff1f;不用重启&#xff0c;服务器会自己悄悄的把更新后的程序给重新加载一遍&#xff0c;这就是热部署。 ​ 热部署的功能是如…...

接口自动化测试框架搭建全部过程

思想&#xff1a; 1、基本目录的搭建 report:静态输出目录(报告或者日志) data&#xff1a;静态输入目录(可以存放Excel数据&#xff0c;被读取的一些数据) utils:实用方法层(这里存放的是项目的公共方法&#xff0c;一般拿到别…...

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 日才正式学习网络安全的&#xff0c;之前在国营单位工作了 4 年&#xff0c;在长沙一个月工资只有 5000 块&#xff0c;而且看不到任何晋升的希望&#xff0c;如果想要往上走&#xff0c;那背后就一定要有关系才行。 而且国营单位的气氛是你干的多了&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都难拿....

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

低代码平台名声臭,用起来却真香——60%开发者不敢承认

群体盲从意识会淹没个体的理性&#xff0c;个体一旦将自己归入该群体&#xff0c;其原本独立的理性就会被群体的无知疯狂所淹没。——《乌合之众》 不知道从什么时候开始&#xff0c;“低代码不行”的论调充斥着整个互联网圈子&#xff0c;csdn、掘金、知乎、B站、脉脉……到处…...

PHP 的代码简洁之道(Clean Code PHP)

介绍 Robert C.Martin’s 的 软件工程师准则 Clean Code 同样适用于 PHP。它并不是一个编码风格指南&#xff0c;它指导我们用 PHP 写出具有可读性&#xff0c;可复用性且可分解的代码。 并非所有的准则都必须严格遵守&#xff0c;甚至一些已经成为普遍的约定。这仅仅作为指导方…...

delphi在两个窗口间用消息通讯

用SendMessage在窗口间通讯&#xff1a; 发送方 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倾斜摄影三维模型是一种基于倾斜摄影技术&#xff0c;通过多个角度拍摄同一区域的影像&#xff0c;利用计算机图像处理和三维重建技术生成的三维地理信息数据。由于一个大区域可能需要多块…...

【科普】PCB为什么常用50Ω阻抗?6大原因

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

Linux嵌入式uboot使用tftp网络启动加载zImage、设备树

文章目录 一、前言二、Linux U-boot 相关命令&#xff08;1&#xff09;help 命令&#xff08;2&#xff09;printenv 命令&#xff08;3&#xff09;setenv 函数&#xff08;4&#xff09;saveenv 函数 三、tftp启动linux内核步骤&#xff08;1&#xff09;进入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. 前言 科技日益发展的今天&#xff0c;移动电子设备似乎成了我们生活的主角&#xff0c;智能…...

后端大厂面试总结大全六

目录&#xff1a; 1、Transactional注解控制事务有哪些不生效的场景2、MySQL的优化 1、Transactional注解控制事务有哪些不生效的场景 数据库引擎不支持事务数据源没有配置事务管理器没有被spring管理方法不是public的同一个类中方法调用&#xff0c;导致Transactional失效 举…...

2023五一数学建模A题B题C题思路模型代码

占个位置吧&#xff0c;开始在本帖实时更新五一数学建模赛题思路代码&#xff0c;文章末尾获取&#xff01; 持续为更新参考思路 赛题思路 会持续进行思路模型分析&#xff0c;下自行获取。 A题思路&#xff1a; &#xff08;比赛开始后第一时间更新&#xff09; B题思路…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...