C++的命名重整

C++ 的 命名重整(Name Mangling) 是编译器将 C++ 中的函数、变量、类等标识符转换为唯一且可链接的符号名的过程。这是由于 C++ 支持函数重载、命名空间、模板、类成员等特性,而底层链接器(如 ELF 链接器)只支持简单的全局符号名。

C++ 一直为人诟病之一的原因是他的二进制模块兼容性不好,即ABI(Application Binary Interface)问题。对于同一源代码,不同编译器,甚至同一编译器不同版本都不兼容,其编译出来的ABI不能相互使用。比如其中一个ABI问题是为了支持函数重载,C++使用了Name Mangling(翻译为命名重整、名字改编、名字修饰等)技术,而Name Mangling在不同编译器间基本是完全不兼容的。

Name Mangling是一种在编译过程中,将函数、变量的名称重新改编的机制,简单来说就是编译器为了区分各个函数,将函数通过一定算法,重新修饰为一个全局唯一的名称。

C++除了支持函数重载,也即是允许多个函数拥有一样的名字,同时也支持命名空间,也即同时允许多个同样的函数定义在在不同的名称空间。这使得Name mangling尤其复杂。


🔍 为什么需要命名重整?

C 语言中:

cpp 复制代码
int add(int a, int b);
float add(float a, float b);  // ❌ 编译错误!C 不支持重载

C++ 中合法:

cpp 复制代码
int add(int a, int b);
float add(float a, float b);  // ✅ 合法!但链接器看到两个 "add" 会冲突

→ 编译器必须把它们变成不同的符号,比如:

  • _Z3addii
  • _Z3addff

这样链接器就能区分了。


🧩 命名重整规则(Itanium ABI)

主流编译器(GCC、Clang、Android NDK、鸿蒙 NDK)都遵循 Itanium C++ ABI 标准。

基本格式:

复制代码
_Z [嵌套名称] [参数编码]

常见编码示例:

C++ 原始代码 Mangled Name 说明
void foo() _Z3foov 3foo = 名字长度+名字,v = void
int add(int, int) _Z3addii i = int
std::string f() _Z1fB5cxx11Ss_Z1fv(旧 ABI) Ss = std::string(新 ABI 用 B5cxx11 表示 inline namespace)
namespace ns { void bar(); } _ZN2ns3barEv N...E 表示嵌套,2ns = "ns" 长度 2
class A { void func(); }; _ZN1A4funcEv 类成员函数
template<typename T> void g(T); g<int>(42); _Z1gIiEvT_ 模板实例化

🔧 常用类型编码(部分)

类型 编码
void v
bool b
char c
int i
unsigned int j
long l
float f
double d
const T& RKT_
T* PT_
std::string Ss(实际是 std::basic_string<char> 的别名)
std::string_view NSt7__n117basic_string_viewIcNS_11char_traitsIcEEEE(简化后常为 St17basic_string_viewIcSt11char_traitsIcEE

💡 注意:std:: 常缩写为 Ststd::__cxx11 可能编码为 St7__n1 或类似。


✅ 如何解码(Demangle)?

方法 1:使用 c++filt(最常用)

复制代码
echo "_ZN3pie13ICacheService2ofERKNSt17basic_string_viewIcSt11char_traitsIcEEEE" | c++filt

输出:

cpp 复制代码
pie::ICacheService::of(std::basic_string_view<char, std::char_traits<char> > const&)

方法 2:在代码中使用 abi::__cxa_demangle(C++ 运行时)

cpp 复制代码
#include <cxxabi.h>
#include <memory>
#include <string>

std::string demangle(const char* name) {
    int status = -1;
    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, nullptr, nullptr, &status),
        std::free
    };
    return (status == 0) ? res.get() : name;
}

方法 3:工具链自带

  • nm -C:自动解码符号(GNU nm)

    bash 复制代码
    nm -C libaccount.so | grep ICacheService
  • objdump -C:反汇编时解码

  • Android Studio / DevEco:调试时自动显示原始函数名


⚠️ 常见问题与注意事项

1. 不同编译器 mangle 规则不同

  • MSVC(Windows)使用自己的规则(如 ?func@@YAXH@Z),不兼容 Itanium ABI
  • GCC/Clang/NDK 使用 Itanium ABI → 兼容

2. ABI 兼容性问题

  • GCC 5+ 引入 _GLIBCXX_USE_CXX11_ABI=1,导致 std::string 符号变为 std::__cxx11::string
  • 如果一个库用新 ABI,另一个用旧 ABI,即使函数存在也会"symbol not found"

3. 静态成员 vs 普通成员

  • 静态成员函数:mangled 名不含 this,类似普通函数
  • 普通成员函数:隐含 this 指针,但不影响符号名(调用约定处理)

4. 内联函数 / 模板可能无符号

  • 如果函数被内联或未实例化,可能不会出现在 .so 的符号表中

🛠️ 实用命令汇总

任务 命令
解码符号 c++filt _Z3foov
查看 .so 符号(已解码) nm -C libxxx.so
查看未定义符号 `nm -u libxxx.so
搜索特定函数 `nm -D libxxx.so
检查是否 strip file libxxx.so → 看是否含 "stripped"

✅ 总结

概念 说明
命名重整 C++ 编译器将函数名转为唯一符号的过程
目的 支持重载、命名空间、模板等特性
标准 Linux/Android/HarmonyOS 使用 Itanium ABI
解码工具 c++filt, nm -C, abi::__cxa_demangle
典型错误 "symbol not found" 往往是 mangled 名不匹配(ABI/版本/缺失)
相关推荐
仰泳的熊猫2 小时前
1148 Werewolf - Simple Version
数据结构·c++·算法·pat考试
chao1898442 小时前
MATLAB中的多重网格算法与计算流体动力学
开发语言·算法·matlab
木盏2 小时前
三维高斯的分裂
开发语言·python
十五年专注C++开发2 小时前
同一线程有两个boost::asio::io_context可以吗?
c++·boost·asio·异步编程·io_context
精神小伙就是猛2 小时前
C# sealed密封 追本溯源
开发语言·c#
真正的醒悟2 小时前
图解网络35
开发语言·网络·php
我是Feri2 小时前
HarmonyOS 6.0 视频播放组件使用教程
华为·音视频·harmonyos
大连好光景2 小时前
批量匿名数据重识别(debug记录)
开发语言·python
计算机毕设VX:Fegn08952 小时前
计算机毕业设计|基于Java + vue水果商城系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·课程设计