C++ 的名字修饰,编译器会把函数名和参数类型拼接成一个新名字,目的是支持函数重载。而 C 语言没有函数重载,编译器对函数名的处理非常简单:直接用原函数名,不会加任何参数相关的修饰。这就导致一个问题:如果 C++ 代码想调用 C 语言写的函数(比如 C 写的库),或者 C 代码想调用 C++ 写的函数,因为名字处理方式不同,链接时会找不到函数(报'未定义的引用'错误)。
extern "C" 的核心作用
extern "C" 是 C++ 的专属语法,它告诉 C++ 编译器:对这个函数,按照 C 语言的规则处理(不做名字修饰),这样就能和 C 语言代码兼容。
简单说:extern "C" 是 C++ 和 C 之间的'翻译官',解决两者函数名不兼容的问题。即改变函数名的编译规则(按 C 语言来),解决 C/C++ 互调的链接问题。
代码示例:怎么用 extern "C"
场景 1:C++ 中调用 C 语言写的函数
假设你有一个 C 语言文件 c_func.c:
// c_func.c - 纯 C 语言代码#include<stdio.h>// C 语言的普通函数,编译器处理后名字就是 addintadd(int a, int b) {
return a + b;
}
然后在 C++ 文件 cpp_main.cpp 中调用这个 C 函数:
// cpp_main.cpp - C++ 代码#include<iostream>usingnamespace std;
// 关键:用 extern "C"声明这个函数是 C 语言风格的(不做名字修饰)extern"C" {
intadd(int a, int b); // 声明 C 语言的 add 函数
}
intmain(){
// 调用 C 语言的 add 函数,链接时会找名字为 add 的函数(而非 C++ 修饰后的名字)
cout << add(3, 5) << endl; // 输出 8return0;
}
场景 2:C++ 中定义能被 C 调用的函数
如果想让 C 代码调用 C++ 写的函数,需要给 C++ 函数加 extern "C":
// cpp_func.cpp - C++ 代码// 告诉编译器:这个函数按 C 规则处理(名字不修饰)extern"C"intmultiply(int a, int b){
return a * b;
}
然后在 C 文件 c_main.c 中调用:
// c_main.c - 纯 C 语言代码#include<stdio.h>// 声明 C++ 写的 multiply 函数(C 语言不认识 extern "C",所以直接声明)externintmultiply(int a, int b);
intmain() {
printf("%d\n", multiply(4, 6)); // 输出 24return0;
}
场景 3:和函数重载的冲突(重点)
extern "C" 和 C++ 的函数重载不能同时用—— 因为 extern "C" 要求函数按 C 规则处理(名字不修饰),而重载依赖名字修饰区分不同函数。
比如下面的代码会报错:
#include<iostream>usingnamespace std;
// 错误:extern "C"的函数不能重载extern"C" {
intadd(int a); // 按 C 规则,名字是 addintadd(int a, int b); // 按 C 规则,名字也是 add → 重定义错误
}
intmain(){
return0;
}
extern "C" 的常见写法
1. 单个函数:直接在声明 / 定义前加 extern "C"
extern"C"intadd(int a, int b);
2. 多个函数:用 extern "C" { ... }包裹
extern"C" {
intadd(int a, int b);
intsub(int a, int b);
}
3. 兼容头文件(既可以被 C 包含,也可以被 C++ 包含)
这是实际项目中最常用的写法,避免 C 编译器不认识 extern "C":
// func.h - 通用头文件#ifndef FUNC_H#define FUNC_H#ifdef __cplusplusextern"C" {
#endif// 函数声明(C 和 C++ 都能识别)intadd(int a, int b);
intsub(int a, int b);
#ifdef __cplusplus
}
#endif#endif// FUNC_H
extern "C"冷门作用 -- 修饰变量
extern "C" 修饰变量的场景远不如函数常见,但原理和函数一致:让 C++ 中的变量按 C 语言的链接规则处理,这样 C 代码能访问 C++ 中的全局变量,反之亦然。
如果不在跨语言访问的变量上添加 extern "C" 会发生什么,核心结论是:C 和 C++ 代码无法正确链接到对方的全局变量,编译时可能无报错,但链接阶段会报「未定义的引用」错误。
场景示例:C++ 中定义变量,让 C 代码能访问
// cpp_var.cpp - C++ 文件,定义能被 C 访问的全局变量#include<iostream>// extern "C":让这个变量按 C 语言的链接规则处理extern"C"int cpp_global = 100;
// func.h - 头文件(声明函数)#ifndef FUNC_H#define FUNC_H// 函数声明默认带 extern 属性,不用显式写intadd(int a, int b);
#endif// FUNC_H// func.cpp - 实现函数#include"func.h"intadd(int a, int b){
return a + b;
}
// main.cpp - 调用函数#include<iostream>#include"func.h"usingnamespace std;
intmain(){
cout << add(3, 5) << endl; // 正常调用return0;
}
extern "C"和extern的结合使用
当你需要:
让函数能被其他文件使用(extern的作用);
同时让这个函数按 C 语言规则编译(extern "C"的作用)。
就需要把两者结合,这也是跨语言调用时的标准写法:
// func.h - 兼容 C/C++的头文件#ifndef FUNC_H#define FUNC_H#ifdef __cplusplus// 1. extern "C":按 C 规则编译;2. 函数声明隐含 extern:让其他文件能用extern"C" {
#endifintadd(int a, int b); // 声明:让其他文件能调用intsub(int a, int b);
#ifdef __cplusplus
}
#endif#endif// FUNC_H// func.cpp - 实现函数#include"func.h"// 这里的 add 会按 extern "C"的规则编译(名字不修饰),同时能被其他文件调用intadd(int a, int b){
return a + b;
}
intsub(int a, int b){
return a - b;
}