为什么 C++ 要改函数名?一文讲透 gcc / g++ / C/C++ 混编本质
2026/5/4 17:08:17 网站建设 项目流程

一、一个奇怪的问题

在做 C/C++ 混编的时候,你可能会遇到一个很诡异的报错:

undefined reference to `hello`

但明明你已经写了这个函数:

// a.c void hello() { printf("hello\n"); }

在 C++ 里调用:

// main.cpp void hello(); int main() { hello(); }

👉 看起来完全没问题,但就是链接失败


二、问题到底出在哪?

很多人第一反应是:

  • 编译器问题?
  • gcc / g++ 用错了?
  • 头文件问题?

👉 其实都不是。


真正原因只有一个:

C 和 C++ 编译后的“函数名不一样”

三、C vs C++ 的根本差异

✅ C语言

void hello();

👉 编译后:

hello

✅ C++语言

void hello();

👉 编译后:

_Z5hellov

函数名被改了!


四、什么是 name mangling(名字修饰)

这就是 C++ 的一个核心机制:

name mangling = 编译时对函数名进行编码

那为什么要改名字?

👉 因为 C++ 支持:

void func(int a); void func(double a);

👉 源码里:

func func

👉 但底层必须区分:

func(int) func(double)

👉 所以编译器会变成:

_Z4funci _Z4funcd

一句话总结

C++ 改名字,是为了支持函数重载

五、混编为什么会报错?

现在问题就清楚了👇


C 编译出来:

hello

C++ 编译出来:

_Z5hellov

👉 当 C++ 去调用:

hello();

👉 它实际在找:

_Z5hellov

👉 但 C 提供的是:

hello

👉 ❌ 找不到 → 报错:

undefined reference

六、extern "C" 的本质

解决方案:

extern "C" { void hello(); }

它到底做了什么?

告诉 C++ 编译器: 👉 这个函数按 C 的规则编译(不要改名字)

👉 结果:

hello

👉 ✔️ 和 C 对齐 → 成功


七、标准写法(必须掌握)

// a.h #ifdef __cplusplus extern "C" { #endif void hello(); #ifdef __cplusplus } #endif

为什么要这样写?

👉 因为:

这个头文件既可能被 C 用,也可能被 C++ 用

八、gcc 和 g++ 的真正区别

很多人会误解:

g++ 会改名字

👉 ❌ 不对


✅ 正确理解

是否改名字 = 取决于“是否按 C++ 编译”

工具默认行为
gcc按 C 编译
g++按 C++ 编译

👉 所以:

g++ 只是“触发 C++ 编译规则”

九、为什么必须用 g++ 链接?

混编还有第二个坑👇


用 gcc 链接:

gcc main.o a.o -o app

👉 报错:

undefined reference to std::cout

用 g++:

g++ main.o a.o -o app

原因

C++ 需要额外运行库:libstdc++

👉 g++ 会自动帮你:

链接 C++ 标准库

十、混编完整流程(工程级)

编译阶段: C → gcc C++ → g++ 链接阶段: 统一 → g++ 符号问题: extern "C"

十一、最终总结(记住这4句话就够了)

1️⃣ C++ 会改函数名(name mangling) 2️⃣ extern "C" 可以关闭改名 3️⃣ gcc / g++ 只是编译入口 4️⃣ 混编必须用 g++ 链接

下一篇预告

如果你觉得这篇有点“突然懂了”,

👉 下一篇我会讲:

编译期 vs 运行时:为什么 C++ 和 Java 的世界完全不同?


写在最后

很多看似零散的问题:

  • gcc / g++
  • extern "C"
  • 混编报错

其实背后只有一个核心:

👉 编译器如何处理“函数符号”

理解这一点,你对系统开发的理解会直接上一个层级。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询