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的更多相关文章

  1. 使用Google产品以来遇到的最糟糕、最霸道、最让人抓狂的设计

    很久没有登录cnblogs@gmail.com这个邮箱,今天通过gmail.com登录了一下,登录后出现一个对话框要求设置性别与出生日期,而且必须要设置,不设置不让登录. 这个邮箱是我们网站用的是邮箱 ...

  2. 让人抓狂的MySQL安装-8.0.12版本

    今天一个下午就做了一件事,把MySQL安装成功,安装的过程让人很狂躁.于是一边骂,一边查错,才把这个软件给安装成功了. 详细的安装步骤,这里就不赘述了.参见https://blog.csdn.net/ ...

  3. oracle 让人抓狂的错误之 null值 与 无值(无结果)-开发系列(一)

    近期.在做开发.写存过的时候碰到一些问题,找了好长时间才发现原因.并且是曾经不知道的. 所以在这给记下来 给自己备忘和大家參考. 一 .null值 以下举个最简单的样例.寻常工作其中肯定比这个sql复 ...

  4. 轻松搞定word中让人抓狂的自动编号

    在word中使用自动编号时,如果一级编号是2,想让其后面的二级编号自动编号为2.1.2.2--,三级编号自动编号为2.1.1.2.1.2--:且在该一级编号调整为3时,后面的二级编号和三级编号的第一位 ...

  5. 苹果应用商店AppStore审核中文指南 分类: ios相关 app相关 2015-07-27 15:33 84人阅读 评论(0) 收藏

    目录 1. 条款与条件 2. 功能 3. 元数据.评级与排名 4. 位置 5. 推送通知 6. 游戏中心 7. 广告 8. 商标与商业外观 9. 媒体内容 10. 用户界面 11. 购买与货币 12. ...

  6. 那年曾让我哭笑不得抓狂的C语言

    1.关于+=以及-= 这是两个运算符,但你否有过这种经历: int temp; char i ;i<MAX;i++) { ... temp=+; //这里本意是每次循环,temp都自增2,但是却 ...

  7. 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 ...

  8. C++雾中风景12:聊聊C++中的Mutex,以及拯救生产力的Boost

    笔者近期在工作之中编程实现一个Cache结构的封装,需要使用到C++之中的互斥量Mutex,于是花了一些时间进行了调研.(结果对C++标准库很是绝望....)最终还是通过利用了Boost库的share ...

  9. 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 ...

随机推荐

  1. 简述BFS与DFS

    简述BFS与DFS 最近学习了数据结构课程以及应对蓝桥杯备考,所以花费了一点时间将比较重要的两个搜索BFS(宽度优先搜索)和DFS(深度优先搜索)大致思路以及代码整理出来,如有错误,还请各位大佬批评改 ...

  2. PHP学习中的一些总结(持续更新)

    文件上传部分 在前台的<form>表单中 hidden隐藏域的MAX_FILE_SIZE可以起到实质性的控制作用,即在文件上传之前就可以判断文件的大小,格式为: <form acti ...

  3. idea 执行maven打包命令时,修改war包名称

  4. 阿里大牛教你基于Python的 Selenium自动化测试示例解析

    今天给大家讲解的是自动化测试示例的解析,如有不对的地方请多多指教. 自动化测试示例如下: from selenium import webdriver from selenium.webdriver. ...

  5. APM姿态控制流程

    对初学者了解控制流程有一定帮助 在主循环执行过程中(比如Pixhawk的任务调度周期2.5ms,400Hz:APM2.x为10ms,100Hz),每一个周期,程序会按下述步骤执行:• 首先,高层次文件 ...

  6. 攻防世界——web新手练习区解题记录<1>(1-4题)

    web新手练习区一至四题 第一题view_source: 题目说右键不管用了,我们先获取在线场景来看一看,我们看到这样一个网页,并且右键确实点了没什么反应,而用到右键一般就是查看网页源码 用快捷键(F ...

  7. T4m

    Unity T4M 中文讲解 http://blog.csdn.net/tianmao111/article/details/46482963

  8. asp image

    @Url.Content("~/Content/images/myimage.png") <img src="~/Content/images/myimage.pn ...

  9. Apollo(阿波罗)配置中心Java客户端使用指南使用指南

          Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用于微服务配置管 ...

  10. 哈希,hash

    Hash,一般翻译做散列.杂凑,或音译为哈希.----摘自百度百科 先来看个题:给你一坨一些键值集<key,value>,\(key\)的范围是\([1,10^{10}]\),每次询问\( ...