欢迎光临
我们一直在努力

浅谈extern “C”:C/C++混合编程

浅谈extern “C”:C/C++混合编程

引言

在很多C/C++的库中,我们可看到如下的结构, 其中__cplusplus是C++编译器的一个预定义宏。只要用C++的编译器(如g++, clang++)编译源代码,就会存在这个宏,但如果是用C的编译器(如gcc,clang)。

#ifndef LIB_H
#define LIB_H

#ifdef __cplusplus
extern "C" {
#endif

/* … */

#ifdef __cplusplus
}
#endif

#endif

其中有部分代码被extern "C" 修饰了, 那extern "C"是什么,他的作用又是什么呢?

首先,extern 是 C / C++ 中的关键字,主要作用是 声明一个在其它文件或其它位置定义的变量或函数,用于实现 跨文件共享。修饰的是对象的链接属性(linkage)。

但是extern和本次要讲的 extern "C"的区别还是蛮大的,extern "C"的主要作用是取消C++的名字修饰(name mangling)机制。** **在使用 extern 指定外部链接的基础上,进一步要求编译器 按照 C 语言的链接规则进行符号链接。

一、为什么需要extern “C”?

C++作为C的超集,在兼容C语法的同时,引入了****函数重载这一核心特性,而这一特性的实现依赖于名字修饰(Name Mangling)机制:

  • C语言:编译器对函数名的处理非常简单,比如定义int sum(int a, int b),编译后符号表中依然是sum;
  • C++语言:为了区分重载函数(比如sum(int)和sum(float)),编译器会将函数名、参数类型等信息拼接成新的名字(不同编译器规则不同,比如sum(int, int)可能被修饰为_Z3sumii)。

这种差异会导致一个问题:如果C++代码直接调用C语言编写的函数,C++编译器会按照自己的规则去符号表中查找修饰后的函数名,但C编译生成的符号是原始名,就像是通信中,双方支持的协议不一样,双方没办法看懂对方的报文(也可以类比不同国家的人,说不同的语言,你只说汉语,我只说英语,就不能正常交流,双方都会罢工,叽里呱啦说了半天,对方根本不懂),最后就会搞出链接器在目标文件里面找不到引用的问题。而extern "C"的核心作用,就是告诉C++编译器:“这段代码按照C语言的规则编译,不要做名字修饰”,这样一来,编译时输出的目标文件,没有被extern "C"标记的地方,任然按照C++的方式爱咋修饰咋修饰,而extern "C"标记的地方, 就按照C语言的方式,不进行名字修饰。

二、使用工具查看符号表验证

假设,我们存在一个如下的项目结构,好吧

.
├── lib.c
├── lib.h
└── main.cpp

然后我们每个文件里面有如下代码

main.cpp

#include <iostream>
#include "lib.h"
using namespace std;

double sum(double a, double b) {
return a + b;
}

int main() {
cout << sum(1, 1);
}

lib.h

#ifndef LIB_H
#define LIB_H

int sum(int, int);

#endif

lib.c

int sum(int a, int b) {
return a + b;
}

然后我们使用g++ -c main.cpp -o main.o 和 gcc -c lib.c -o lib.o生成目标文件main.o , lib.o。 在这里插入图片描述

再然后我们使用nm命令查看两个目标文件的符号表.

nm main.o对应的输出如下

这里我们可以看到两个名字,__Z3sumdd对应了我们main.cpp里面定义的double sum(double, double)函数, __Z3sumii则对应了我们main.cpp里面引用了但暂时没有找到定义的int sum(int, int)函数

nm lib.o对应的输出如下

在这里插入图片描述在这里插入图片描述

__sum则对应了我们lib.c里面定义的int sum(int, int)函数, 可以看到我们的函数在不同的规则下,在符号表里面的名字是不一致的。当我们链接的时候,就会报错。 在这里插入图片描述

可以看到我们这里因为链接器找不到sum(int, int)函数的定义而报错,但也发现了,在lib.o里面有__sum这个名字,可能是缺少了extern "C"

那我们在lib.h补上extern "C"

lib.h

#ifndef LIB_H
#define LIB_H

#ifdef __cplusplus
extern "C" {
#endif

int sum(int a, int b);

#ifdef __cplusplus
}
#endif
#endif

就像这样子, 然后我们再编译, 就发现可以通过了。 在这里插入图片描述 接下来我们再次查看main.o的符号表,可以发现此时的符号表就没有对int sum(int, int)进行名字修饰,所以链接的时候也能正确的匹配到lib.o里面定义的int sum(int, int)。

在这里插入图片描述

三、extern "C"的基本用法

1. 修饰单个函数/变量

最基础的用法是为单个函数或变量指定C语言的编译规则,语法格式为:

// C++文件中调用C函数的声明
extern "C" {
// 声明C语言编写的add函数
int add(int a, int b);
}

// 也可以修饰单个函数
extern "C" void print_msg(const char* msg);

2. 修饰头文件

实际开发中,没人会愿意挨个挨给C函数加上extern "C",而是直接包裹整个头文件的内容,确保头文件内的所有函数/变量都按C规则处理:

// C++文件中包含C语言头文件
#ifdef __cplusplus
// __cplusplus是C++编译器的内置宏,仅在C++编译环境下定义
extern "C" {
#endif

// 引入C语言头文件(比如自己写的c_math.h,或系统的stdio.h等)
#include "c_math.h"

#ifdef __cplusplus
}
#endif

这种写法是****跨语言兼容的最佳实践:当这段代码被C编译器处理时,__cplusplus未定义,会跳过extern "C";当被C++编译器处理时,会启用C语言编译规则。

3. C++中定义供C调用的函数

如果需要在C++中编写函数,让C代码调用,也需要用extern "C"修饰函数定义:

// cpp_file.cpp
#include <stdio.h>

// 告诉编译器按C规则编译这个函数
extern "C" void hello_from_cpp() {
printf("Hello from C++ (but compiled as C)\\n");
}

编译时,C++编译器会为hello_from_cpp生成C风格的符号,C代码就能通过声明直接调用:

// c_file.c
// 声明C++中定义的函数
void hello_from_cpp();

int main() {
hello_from_cpp(); // 正常调用,输出对应内容
return 0;
}

四、extern “C” 核心注意事项

1. 必加场景

  • C++ 调用 C 代码(头文件声明加)
  • C 调用 C++ 代码(C++ 实现侧/声明加)
  • 跨语言调用 C 库(保证符号名不变)。

2. 语法必守

  • **必须用 **#ifdef __cplusplus 包裹,避免 C 编译器报错;
  • 可批量包裹多个函数/全局变量声明,无需逐个加;
  • 块内****不能写 C++ 特有语法(重载、类、模板等),C 不支持会编译失败。

3. 关键特性

  • 仅对 C++ 编译器生效,C 编译时完全忽略
  • 修饰后函数****失去 C++ 重载特性(按 C 命名规则编译)
  • 只对函数/全局变量有效,typedef/struct 等类型定义加了无意义。

五、参考文献

**[1] cppreference.com. C++ keyword: extern[EB/OL]. **https://cppreference.com/w/cpp/keyword/extern.html (2026-01-06)

**[2] cppreference.com. Storage class specifiers[EB/OL]. **https://cppreference.com/w/cpp/language/storage_duration.html (2026-01-06)

**[3] Stack Overflow. What is the effect of extern “C” in C++?[EB/OL]. **https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c (2026-01-07)

[4] Stephen Prata. C++ Primer Plus(six edition)[M]. 张海龙, 等译. 北京: 人民邮电出版社, 2012

赞(0)
未经允许不得转载:171主机测评 » 浅谈extern “C”:C/C++混合编程
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址