C++雾中风景15:聊聊让人抓狂的Name Mangling
Name Mangling,直接翻译过来为名字改写 。它是深入理解 C++ 编译链接模型的必由之路。
笔者近期进行数据库开发工作时,涉及到MySQL客户端的编译链接的问题,通过重新厘清了之前理解一知半解的Name Manging,解决了让人抓狂的编译链接问题。
接下来,和大家聊聊C++的Name Mangling。
1.什么是Name Mangling
1.1 Name Mangling的作用
在进行编程的过程之中,我们常常遇见变量或函数重名的情况。比如:函数的重载,或通过不同程序块与命名空间变量与函数的重名。
而在出现变量或函数名相同的情况下,编译器进行代码编译时需要保证变量与函数的签名的全局唯一性。如果无法进行上述保证,在链接阶段就会产生链接的二义性,会导致编译器不知道应该如何取用正确的变量与函数符号的内存地址。
为了解决上述问题,编译器实现了一种叫做Name Mangling的方式:它通过一个固定的命名规则来重新组织源代码之中我们定义的变量名和函数名,来确保了能够将被链接的目标文件中的符号签名的唯一性。(由于在C++的标准之中,并未强制规定Name Mangling的实现机制,所以不同的编译器在不同的平台上实现是完全不同的。笔者的后续关于Name Mangling的讲解将基于Linux上的GCC展开。)
1.2 举个栗子
上述内容讲明白了Name Mangling的意义,我们来通过实际的代码来瞅瞅它是如何生效的。
首先看看如下代码:
#include <iostream>
#include <string>
#include <vector>
namespace Happen {
struct MyClass {
std::vector<std::string> _str_vec;
};
}
int main() {
Happen::MyClass myClass;
return 0;
}
接下来,我们使用g++获取它的汇编代码
g++ -S main.cpp
使用编辑器打开生成的main.s文件,我们就可以看到下面这些被Name Mangling之后的命名了。
call _ZN6Happen7MyClassC1Ev
movl $0, %ebx
leaq -48(%rbp), %rax
movq %rax, %rdi
call _ZN6Happen7MyClassD1Ev
这里可以看到,代码调用了_ZN6Happen7MyClassC1Ev与_ZN6Happen7MyClassD1Ev这两个函数。这其实就是代码之中调用了我们定义的MyClass的构造函数与析构函数。而这里令人望而生畏的命名就是Name Mangling的功劳啦~~
2. Name DeMangling
既然有了Name Mangling了,自然就要有Name DeMangling。上述_ZN6Happen7MyClassC1Ev,否则你确定你能看懂下面的这一长串Name Mangling之后的结果:
MN6Happen7MyClassESt6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS7_EE
2.1 通过API进行Name DeMangling
在C++之中,我们常常使用typeid,来获取类型的type_info信息,而Name Mangling就包含在type_info之中,我们来看如下代码:
#include <iostream>
#include <string>
#include <vector>
namespace Happen {
struct MyClass {
std::vector<std::string> _str_vec;
};
}
int main() {
std::cout << typeid(&Happen::MyClass::_str_vec).name() << "\n";
return 0;
}
它的输出正是上面那串让人「抓狂」的命名,我们现在尝试通过GNU的API来脱掉它的马甲,真正的看看它到底是啥。
这里使用了abi::__cxa_demangle来获取DeMangling时真正的结果。
#include <iostream>
#include <string>
#include <vector>
#include <cxxabi.h>
namespace Happen {
struct MyClass {
std::vector<std::string> _str_vec;
};
}
int main() {
char* real_name = abi::__cxa_demangle(typeid(&Happen::MyClass::_str_vec).name(), \
nullptr, nullptr, nullptr);
std::cout << real_name << "\n";
return 0;
}
这是通过Name DeMangling实际输出的结果。(囧rz,好像可读性也并没有太好,C++的类型系统实在是太复杂了,不过起码能让我们看清楚真正的名字是啥了。)
std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > Happen::MyClass::*
2.2 使用nm或c++filt进行Name Demangling
通过代码进行名字辨析确实会带来诸多不便,所以Linux提供了两个好用的工具:
nm与c++filt,它们可以作用在二进制文件,函数链接库等之上(nm其实就是name mangling的缩写)
通过nm的-C参数就可以直接输出name demangling之后的结果了。
nm -C bin/.so/.a
或者也可以通过c++filt来实现同样的功能
nm bin/.so/.a | c++filt
3.C语言的Name Mangling
C++能够支持调用C语言的函数,同样也支持实现函数库被C语言调用,这个过程之中就涉及到两种语言交互的Name Mangling了。(这个问题会常常导致编译时出现令人抓狂的undefined reference to 『xxx』, 很多时候会让人丈二和尚摸不着头脑)
3.1 两者的区别
由于C语言不支持函数重载,命名空间,类等逻辑,所以C语言的Name Mangling比C++简单很多。我们来看看通过gcc和g++的编译结果有和不同吧,首先我们定义一个简单的函数sum:
int sum(int a, int b) {
return a + b;
}
- g++的编译结果
_Z3sumii - gcc的编译结果
sum
这里可以明显看到二者的不同,由于C++支持函数重载。所以需要在Name Mangling时添加参数的信息,也就是后面的两个ii,指代两个int类型。
3.2 extern "C"
所以通过C++定义的函数需要被C语言调用时,需要通过keyword:extern C来显式的让编译器明白需要使用C语言的Name Mangling规则,以便编译器链接时能够正确的识别函数签名来定位到所需的函数。
extern "C" {
int sum(int a, int b) {
return a + b;
}
};
将上述函数改写为上面的方式之后,通过g++编译的结果也变为了我们所期待的sum了。
4.小结
C++的编译链接问题常常让人抓狂,很多时候如果没有深入了解这个过程之中的逻辑,很容易陷入困境。本篇聊了聊笔者在遇到编译问题时学习Name Mangling来最终解决问题的学习小结。
希望大家能够有所收获,笔者水平有限。成文之处难免有理解谬误之处,欢迎大家多多讨论,指教。
C++雾中风景15:聊聊让人抓狂的Name Mangling的更多相关文章
- 使用Google产品以来遇到的最糟糕、最霸道、最让人抓狂的设计
很久没有登录cnblogs@gmail.com这个邮箱,今天通过gmail.com登录了一下,登录后出现一个对话框要求设置性别与出生日期,而且必须要设置,不设置不让登录. 这个邮箱是我们网站用的是邮箱 ...
- 让人抓狂的MySQL安装-8.0.12版本
今天一个下午就做了一件事,把MySQL安装成功,安装的过程让人很狂躁.于是一边骂,一边查错,才把这个软件给安装成功了. 详细的安装步骤,这里就不赘述了.参见https://blog.csdn.net/ ...
- oracle 让人抓狂的错误之 null值 与 无值(无结果)-开发系列(一)
近期.在做开发.写存过的时候碰到一些问题,找了好长时间才发现原因.并且是曾经不知道的. 所以在这给记下来 给自己备忘和大家參考. 一 .null值 以下举个最简单的样例.寻常工作其中肯定比这个sql复 ...
- 轻松搞定word中让人抓狂的自动编号
在word中使用自动编号时,如果一级编号是2,想让其后面的二级编号自动编号为2.1.2.2--,三级编号自动编号为2.1.1.2.1.2--:且在该一级编号调整为3时,后面的二级编号和三级编号的第一位 ...
- 苹果应用商店AppStore审核中文指南 分类: ios相关 app相关 2015-07-27 15:33 84人阅读 评论(0) 收藏
目录 1. 条款与条件 2. 功能 3. 元数据.评级与排名 4. 位置 5. 推送通知 6. 游戏中心 7. 广告 8. 商标与商业外观 9. 媒体内容 10. 用户界面 11. 购买与货币 12. ...
- 那年曾让我哭笑不得抓狂的C语言
1.关于+=以及-= 这是两个运算符,但你否有过这种经历: int temp; char i ;i<MAX;i++) { ... temp=+; //这里本意是每次循环,temp都自增2,但是却 ...
- PIE(二分) 分类: 二分查找 2015-06-07 15:46 9人阅读 评论(0) 收藏
Pie Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submissio ...
- C++雾中风景12:聊聊C++中的Mutex,以及拯救生产力的Boost
笔者近期在工作之中编程实现一个Cache结构的封装,需要使用到C++之中的互斥量Mutex,于是花了一些时间进行了调研.(结果对C++标准库很是绝望....)最终还是通过利用了Boost库的share ...
- hdu1158 Employment Planning 2016-09-11 15:14 33人阅读 评论(0) 收藏
Employment Planning Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Othe ...
随机推荐
- 牛客网PAT练兵场-科学计数法
题目地址:https://www.nowcoder.com/pat/6/problem/4050 题解:模拟题 /** * Copyright(c) * All rights reserved. * ...
- Mysql如何将某个字段的值,在原有的基础上+1?
Eg: 电商项目中,需要统计某件商品的购买数量问题,这时产品提了一个bug,告诉你我需要你做一个购买数量统计?你会怎么做呢? 这里我只说我自己的思路,首先是浏览加购物车,创建订单并支付,mq消息消费后 ...
- kotlin 作用域函数 : let、run、with、apply、 also、takeIf、takeUnless
1.官方文档 英文: https://kotlinlang.org/docs/reference/scope-functions.html 中文: https://www.kotlincn.net/d ...
- The Triangle (简单动态规划)
7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 (Figure 1) Figure 1 shows a number triangle. Write a program that calc ...
- 【问题】Java和Scala混合编译下无法正常使用lombok的问题
工作中有java和scala和混合编译的工程,最近遇到一个问题,就是工程中有依赖java bean的scala文件,编译过程中发现编译器无法找到Java bean 中 lombok生成的getter, ...
- Docker+Kubernetes/K8s+Jenkins视频资料【干货分享】
不管你是否意识到,在这几年时间里,技术岗招聘重点关注的是Docker.Kubernetes(以下简称: K8S),面试10家公司得有8家会问你会不会"docker"."k ...
- 「面向 offer 学算法」笔面试大杀器 -- 单调栈
目录 前言 单调栈 初入茅庐 小试牛刀 打怪升级 出师试炼 前言 单调栈是一种比较简单的数据结构.虽然简单,但在某些题目中能发挥很好的作用. 最近很多大厂的笔试.面试中都出现了单调栈的题目,而还有不少 ...
- 写Junit测试时用Autowired注入的类实例始终为空怎么解?
踩坑半天多,终于在网上寻觅到了解决方案,特此分享一下. 重要前提:src/main/java下的根包名必须和src/test/main的根包名完全一致,否则就会发生死活不能注入的情况,要继续进行下面的 ...
- JVM 中的对象及引用
JVM中对象的创建过程 对象的内存分配 虚拟机遇到一条 new 指令时,首先检查是否被类加载器加载,如果没有,那必须先执行相应的类加载过程. 类加载就是把 class 加载到 JVM 的运行时数据区的 ...
- C#的TextBox的四种禁止编辑方法
前言 一般而言,Textbox中有两个属性可以对其进行防止编辑的设定,这是最基础的知识,也是我要提出的前两种方法.而后两种方法实际为一种,但可以应用于不同环境中. 一.ReadOnly属性 这样设置, ...