C++ :Symbol:符号

news2025/7/27 21:50:12

1:符号的概念

符号(symbol)是在 ELF格式中会遇到的概念,也就是在写汇编代码时候会遇到的,而在更高级语言(C或者C++)中不会直接遇到这个概念,我们把讨论的范围限制在 Linux上的ELF格式。

符号的绑定(Symbol Binding)符号和符号之间是不一样的,首先我们明确,链接器的任务是把很多个.o文件(object file)组合到一起,因为一个符号可能在某一个 object file中定义了,而在另一个object file 中使用了,链接器的任务就是把这些 reference都分析清楚。

常见的符号有三种类型:

  1. Local Symbol : 只有当前object file 文件才能看见,其他别的 object file看不到
  2. Global Symbol : 所有 object file 里都能看见,全局只能有一个
  3. Weak Symbol : 所有Object file 里都能看见,全局可以有很多,但最后只保留一个,如果有同名Global Symbal,就只留下 Global Symbol

看下面一个例子:

// 我们在 symbol.cpp 源文件中定义一个函数 func
symbol.cpp

int func() {
	return 1;
}

 经过 gcc -c -S main.cpp -o maini.o 命令编译后生成 main.o文件  //  然后 cat main.o 得到上面汇编代码

可以看到 函数名称 func就是一个符号,如果想要调用这个函数,就可以利用这个函数的符号

有人说,汇编后的代码没有看到 func 符号,其实这是C++s实现了函数的重载

2:符号的链接

 下面在介绍下,在链接中可能会出现问题。看下面的代码

 // a.h
#pragma once
int func(){
    return 0;
}


// A.cpp

#include"a.h"
void printl_Fun() {
	func();
}

// main.cpp


#include"a.h"

int main() {
	func();
	return 0;
}

很显然:如果你编译两个.cpp文件,那么就是在编译 A.cpp和main.cpp的时候分别生成 Global Symbol  (func) ,这样在链接的时候就会报错: func 重定义

2.1:编译A.cpp

 2.2 编译main.cpp

 2.3 将A.o 和 main.o 编译后的产物链接起来

 显然报错了:multipe definition of 'func()' 重复定义函数 func()

3: 解决multipe definition问题

3.1  只在一个翻译单元中给出函数的定义,这样的话就只有一个 Global Symbol生成,就不会有问题

// A.h
#pragma once
int func();

// A.cpp

#include"a.h"
int func()
{
	return 0;
}


// main.cpp

#include<iostream>
#include"a.h"

int main() {
	int ret = func();
	std::cout << ret << std::endl;
	return 0;
}

看下预编译和编译成汇编的结果:在main.o中是看不到 全局符号(global symbol) func的

 在看下 A.o的汇编结果: 很显然是存储 全局符号(Global Symbol)func 

所以这就很清晰了,符号 func 在全局符号表中只有一个,链接的时候,自然就不会后什么问题。 

3.2 变成两个 Local Symbol

这样做最后的二进制文件会变大一些. 具体在 C++ 中有两种做法, 拿我们的 func 函数示例. 可以加上 static 这个在C++层面称之为: Internal Linkage

// a.h


#pragma once

//static int func() {
//	// static修饰的 函数,会生成local symbol
//	return 1;
//};

// 方式二 : 匿名的 namespace里面都是 Local Symbal符号
namespace {
	int func() {
		return 1;
	}
}

A.cpp

#include<iostream>
#include"a.h"
void printf_fun()
{
	std::cout << func() << std::endl;
}

// main.cpp

#include<iostream>
#include"a.h"

int main() {
	int ret = func();
	std::cout << ret << std::endl;
	return 0;
}

先看下A.cpp 编译后的产物

 在看下main.cpp 编译后产物

 很显然我们在main.o 和A.o中并没有看到 globl symbol符号,那么当然也不会出现 multipe definition定义,自然就会正常输出 .

3.3 变成两个 Weak Symbol 这个可以直接用编译器拓展 __attribute__((weak))

  • 但是更加常见的操作是: 使用 inline 修饰一个函数
  • 对于这个函数, 编译器会考虑是否内联它. 如果编译器决定不内联, 就会生成一个 Weak Symbol.如果编译器决定内联, 这样即使是在多个翻译单元都定义了, 也不会有重定义的错误.
  • 链接器则会从不同 object file 中随机选择一份 func 的副本. (具体选哪个要看链接器的实现了, 你要做的是确保每个副本是一样的)
// a.h 

#pragma once

// 方式一:只声明
// int func();

// 方式二 : 添加static 变成 local symbol符号
//static int func() {
//	// static修饰的 函数,会生成local symbol
//	return 1;
//};

// 方式二 : 匿名的 namespace里面都是 Local Symbal符号
//namespace {
//	int func() {
//		return 1;
//	}
//}

// 方式三 :添加 inline 修饰符,变成 Weak Symbol
inline int function() {
	return 2;
}


// A.cpp
#include<iostream>
#include"a.h"
void printf_fun()
{
	std::cout << function() << std::endl;
}


// main.cpp
#include<iostream>
#include"a.h"

int main() {
	int ret = function();
	std::cout << ret << std::endl;
	return 0;
}

 4: 总结:

这三种处理方案, 一般使用 1, 3 是比较常见的. 第 2 种的话, 每一个翻译单元都有一份副本, 会增加最终生成二进制的体积和符号个数.

5:扩展

如果你在 struct/class/union 中给出了函数的完整定义, 那么它也是隐式 inline 的. 比如

struct A {
    int func() { return 0; } // 隐式 inline, 所以不会有重定义的错误
};
  1. 现在的 inline 语义大概就是: 允许同一个定义在不同的翻译单元出现, 但你需要确保不同翻译单元给出的定义是一致的. 可以看出, 最自然的实现方案就是使用 ELF 格式中的 Weak Symbol. 
  2. 在 C++ 之中, 除了 inline 会生成 Weak Symbol, 模板生成的内容也会 Weak Symbol. 所以模板可以放在头文件中, 而不用有担心重定义的错误. 事实上, 模板的定义也需要放在头文件中, 不然无法实例化. (除了显式实例化等情况)
  3. 参考文献 ELF 格式的 Symbol 及 C++ 的 inline 关键字 - 知乎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/16594.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python多分支选择结构实例讲解

多分支选择结构的语法格式如下&#xff1a; if 条件表达式 1 : 语句 1/语句块 1 elif 条件表达式 2: 语句 2/语句块 2 elif 条件表达式 n : 语句 n/语句块 n [else: 语句 n1/语句块 n1 ] 【注】计算机行业&#xff0c;描述语法格式时&#xff0c;使用中…

Word处理控件Aspose.Words功能演示:使用 C# 在 Word 文档中创建条形码

条形码是具有编码数据/信息的平行线、点或矩形形式的图像。行业专业人士使用条形码嵌入和访问产品信息、跟踪产品移动并跟上库存。在某些情况下&#xff0c;我们可能需要在 MS Word 文档中生成和添加条形码。MS Word 是最流行和广泛使用的图形文字处理程序。它用于创建带有文本…

总结:从实模式到保护模式的流程和相关寄存器,相关数据结构之间的联系

总结&#xff1a;从实模式到保护模式的相关寄存器和相关数据结构之间的联系 1.怎么进入保护模式 段描述符 段描述符&#xff1a; 实模式下的用户可以破坏存储代码的内存区域 &#xff0c;用段描述符来对某一段进行描述内存段类型属性来阻止这种行为。8个字节大小 全局描述…

[附源码]java毕业设计朋辈帮扶系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

HTML基础

文章目录一、HTML结构1、认识HTML标签2、HTML文件基本结构3、标签层次结构二、HTML常见标签1、注释标签2、标题标签3、段落标签4、换行标签5、格式化标签6、图片标签7、超链接标签8、表格标签9、列表标签10、表单标签12、 div & span一、HTML结构 1、认识HTML标签 HTML 代…

光点高校数据中台,助力高校信息化迎来发展新格局_光点科技

数据是教育信息化改革创新过程中的核心资产。从大数据的角度构建高校数据治理体系&#xff0c;支持高校管理信息向智能化服务和教育数字化转型&#xff0c;已成为必然趋势。 然而&#xff0c;高校拥有更大的数据规模、更复杂的数据类型、更密集的数据交换和对数据治理的需求。因…

从各大论坛收集整理的八股文手册,肝完横躺95%的Java面试岗位

今年的秋招很多小伙伴收获不错&#xff0c;拿到了心仪的offer。也有很多小伙伴屡屡碰壁&#xff0c;选择待在舒适区&#xff0c;不过没关系&#xff0c;错过了今年的金九银十&#xff0c;来年的春招再战呗&#xff01; 最近在各大论坛和社区里看见不少小伙伴慷慨地分享了常见的…

JS正则表达式

文章目录1、创建正则表达式的方式1.1、字面量形式1.2、对象2、正则方法2.1、exec()2.2、test()2.3、可以用于正则的string方法2.3.1、match()2.3.2、replace()补充&#xff1a;$在正则替换中的使用2.3.3、search()2.3.4、split()2.3.5、matchAll()3、边界量词4、模式修饰符5、原…

拷贝构造函数详解

拷贝构造函数详解1.概念与特征2.浅拷贝3.深拷贝4.拷贝构造函数典型调用场景&#xff1a;如果一个类中什么成员都没有&#xff0c;那么该类简称为空类。而空类中其实并不是真的什么都没有&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下6个默认成员函数。构造…

对GROUP BY的增强

使用ROLLUP操作产生求和值使用CUBE操作产生使用GROUPING函数确定该行值是由ROLLUP或者CUBE创建的使用GROUPING SETS生成一个简单的结果集[oracleoracle-db-19c ~]$ [oracleoracle-db-19c ~]$ sqlplus / as sysdbaSQL*Plus: Release 19.0.0.0.0 - Production on Fri Nov 18 10:…

idea2022.2.3版本下载安装配置(包含运行第一个java程序教程)详细步骤

目录 一、下载idea 1.去浏览器搜索idea官网&#xff0c;找到官网 2.选择Download 3.点击左边的Download下载就好了 4.在本地磁盘建一个文件夹&#xff0c;专门用来存放idea软件的 二、安装idea 5.在本地磁盘中的下载里面找到下载的idea并双击打开 6.选择Next ​编辑 7…

【无标题】PCIe收发卡设计资料原理图:611-基于VU9P的2路4Gsps AD 2路5G DA PCIe收发卡

基于VU9P的2路4Gsps AD 2路5G DA PCIe收发卡一、板卡概述 基于XCVU9P的5Gsps AD DA收发PCIe板卡。北京太速科技该板卡要求符合PCIe 3.0标准&#xff0c;包含一片XCVU9P-2FLGA2014I、2组64-bit/8GB DDR4、2路高速AD&#xff0c; 2路高速DA&#xff0c;支持外触发&#xff…

基于单片机的学生视力保护仪

目录 摘 要 1 ABSTRACT 2 第一章 绪论 4 1.1课题的选题背景 4 1.2国内外发展现状 5 1.3本论文主要研究内容 6 1.3.1主要内容 6 1.3.2基本要求 7 第二章 学生视力保护仪总系统设计 8 2.1系统总框图 8 2.2单片机的选择 8 2.3传感器的选择 9 2.4编程语言的选择 9 第三章 系统硬件…

CentOS 7 源码制作ngnx-1.22.1-ipv6 rpm —— 筑梦之路

源码包&#xff1a;http://nginx.org/packages/centos/7/SRPMS/nginx-1.22.1-1.el7.ngx.src.rpm # 安装依赖包yum install http://mirror.centos.org/centos/7/os/x86_64/Packages/pcre2-devel-10.23-2.el7.x86_64.rpm# 安装源码包 初始化项目rpm -ivh nginx-1.22.1-1.el7.ngx…

5G无线技术基础自学系列 | 物理下行控制信道

素材来源&#xff1a;《5G无线网络优化实践》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 2.4.1 PDCCH位置定义 在LTE中&#xff0…

ESP32的AP模式使用

WifiClient 和WebServe有啥区别? (14 封私信 / 1 条消息) httpclient 和webservice有啥区别? - 知乎 (zhihu.com) 1.Webservice两大核心&#xff1a;soap&#xff08;理解为要传输的数据&#xff09;、wsdl&#xff08;理解为传输数据xml的说明&#xff09; soap xml ht…

Java类变量和类方法(static)

文章目录类变量-提出问题类变量内存布局如何定义类变量如何访问类变量类变量使用细节类方法介绍类变量-提出问题 提出问题的主要目的就是让大家思考解决之道&#xff0c;从而引出我要讲的知识点. 说:有一群小孩在玩堆雪人,不时有新的小孩加入&#xff0c;请问如何知道现在共有…

客服系统Golang源码

客服系统 概述 采用Golang语言&#xff0c;基于 gRPC [Mongo Vue3WebsocketRabbitMQ]来实现系统 用户侧逻辑采用匿名注册模式&#xff0c;Token采用JWT技术&#xff0c;服务端不保存Token信息&#xff0c;用户每次使用检测Token合法时返回新的Token来达到续期的目的 后端分…

kubernetes集群基于kubeadm部署以及常见问题解决

文章目录集群类型主机规划环境初始化检查操作系统版本关闭防火墙设置主机名主机名解析时间同步关闭 SELinux关闭 swap 分区将桥接的IPv4流量传递到iptables的链开启ipvs安装容器运行时&#xff08;Docker&#xff09;卸载Docker旧版本&#xff1a;安装 gcc 相关安装Docker设置阿…

Vivado_AXI Quad SPI_IP核

填一下前面的坑。 介绍关于Vivado中AXI Quad SPI v3.2的使用方法。 参考资料&#xff1a;pg153-axi-quad-spi.pdf&#xff0c;可自行在官网下载。 以该IP核的Standard SPI Mode的使用为例。 Address Space OffsetRegister NameAccess TypeDefault Value (hex)Description40hS…