C++ :Symbol:符号
创始人
2024-01-31 17:41:53

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.cppint 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
#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
#include"a.h"
void printf_fun()
{std::cout << func() << std::endl;
}// main.cpp#include
#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
#include"a.h"
void printf_fun()
{std::cout << function() << std::endl;
}// main.cpp
#include
#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 关键字 - 知乎

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
ppt插入pdf ppt根据页... PPT目录页的实际内容,往往只有几行字,怎么摆都感觉有点单薄。因此我做了这个按钮,点一下,自动识别内...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
ppt插入pdf ppt根据页... PPT目录页的实际内容,往往只有几行字,怎么摆都感觉有点单薄。因此我做了这个按钮,点一下,自动识别内...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...