名称的特殊处理(Name Mangling)
先说一个事情, mangle 的意思是 vt.乱砍, 损坏; n. 碾压机。 这意味着 name mangling 就是要先把你精心想出的名字们碾碎, 再拼成独一无二的样子, 当然这么残忍的事情都是编译器瞒着你做的。
一般而言, member 的名称前会被加上 class 名称, 形成独一无二的命名, 例如:
class Bar(){public: int ival;}; //其中的 ival 可能变成这样: //member 经过 name-mangling 后的结果之一 ival__3Bar
编译器你为何要这样? 请考虑这样的派生操作:
class Foo: public Bar{public: int ival;}; //所以必须 name-mangling 为: class Foo { public: int ival__3Bar; int ival__3Foo; };
而对于函数, 因为 member function 可以被重载化, 所以需要更广泛的 magling 手法(更残酷的碾碎, 嗯嗯), 如:
class Point { public: void (float newX); float x(); ... }; //转化为 class Point { public: //这样能行? void x__5Point(float newX); float x__5Point(); ... };
函数如果这么转化, 会导致被重载化的函数实体拥有相同的名称, 为了让它们独一无二, 唯有再加上它们的参数链表(可以从参数原型得到)。 如果把参数类型也编码进去, 就一定可以制造出独一无二的效果, 使我们的 x() 函数有良好的转换(但如果你声明 extern "C" , 就会压抑 nonmember functions的 mangling 效果):
class Point { public: void x__5PointFf(float newX); float x__5PointFv(); ... };
以上所示的只是 cfront 的编码方法, 目前的编译器并没有统一的编码方法。
把参数和函数名称编码在一起, 编译器于是在不同的编译模块之间达成了一种有限的类型检验。 如:
void print(const Point3d&){}
但意外的被这样声明和调用:
void print(const Point3d&);
两个实体如果拥有独一无二的 name-mangling, 那么任何不不正确的调用操作在链接时期就因无法决议而失败。有时候我们把它称为 “确保类型安全的链接行为”。 我觉得这样很乐观, 因为它只可以捕捉函数的标记(或叫做函数签名, 就是指 函数名称 + 参数数目 + 参数类型)错误: 如果 “返回类型” 声明错误, 就没办法检查出来!
在当前的编译系统中, 有一种所谓的 demangling 工具, 用来拦截名称并将其转换回去, 使用者仍然可以处于 “不知道类型名字的极大幸福之中”。
因为 name-mangling 的手法残忍, 结果不堪, 所以一般不会给使用者看到。