跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

C/C++ 中 extern 关键字详解:函数与变量修饰

C/C++ 中 extern 关键字的用法。重点讲解 extern "C" 如何解决 C/C++ 互调时的名字修饰问题,避免链接错误,并说明其与函数重载的冲突。同时阐述 extern 修饰函数和变量在多文件编程中的共享机制,区分声明与定义。通过代码示例展示兼容头文件写法、全局变量访问场景及常见易错点。

DockerOne发布于 2026/3/21更新于 2026/9/11101 浏览

说到 extern 关键字,一般就想到三种情况:

  1. extern 修饰函数
  2. extern 修饰变量
  3. extern "C"

我们先来讲 extern "C"

核心背景

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 语言的普通函数,编译器处理后名字就是 add
int add(int a, int b) {
    return a + b;
}

然后在 C++ 文件 cpp_main.cpp 中调用这个 C 函数:

// cpp_main.cpp - C++ 代码
#include <iostream>
using namespace std;

// 关键:用 extern "C"声明这个函数是 C 语言风格的(不做名字修饰)
extern "C" {
    int add(int a, int b); // 声明 C 语言的 add 函数
}

int main() {
    
    cout << (, ) << endl; 
     ;
}
// 调用 C 语言的 add 函数,链接时会找名字为 add 的函数(而非 C++ 修饰后的名字)
add
3
5
// 输出 8
return
0
场景 2:C++ 中定义能被 C 调用的函数

如果想让 C 代码调用 C++ 写的函数,需要给 C++ 函数加 extern "C":

// cpp_func.cpp - C++ 代码
// 告诉编译器:这个函数按 C 规则处理(名字不修饰)
extern "C" int multiply(int a, int b) {
    return a * b;
}

然后在 C 文件 c_main.c 中调用:

// c_main.c - 纯 C 语言代码
#include <stdio.h>

// 声明 C++ 写的 multiply 函数(C 语言不认识 extern "C",所以直接声明)
extern int multiply(int a, int b);

int main() {
    printf("%d\n", multiply(4, 6)); // 输出 24
    return 0;
}
场景 3:和函数重载的冲突(重点)

extern "C" 和 C++ 的函数重载不能同时用—— 因为 extern "C" 要求函数按 C 规则处理(名字不修饰),而重载依赖名字修饰区分不同函数。

比如下面的代码会报错:

#include <iostream>
using namespace std;

// 错误:extern "C"的函数不能重载
extern "C" {
    int add(int a);       // 按 C 规则,名字是 add
    int add(int a, int b); // 按 C 规则,名字也是 add → 重定义错误
}

int main() {
    return 0;
}
extern "C" 的常见写法
1. 单个函数:直接在声明 / 定义前加 extern "C"
extern "C" int add(int a, int b);
2. 多个函数:用 extern "C" { ... }包裹
extern "C" {
    int add(int a, int b);
    int sub(int a, int b);
}
3. 兼容头文件(既可以被 C 包含,也可以被 C++ 包含)

这是实际项目中最常用的写法,避免 C 编译器不认识 extern "C":

// func.h - 通用头文件
#ifndef FUNC_H
#define FUNC_H

#ifdef __cplusplus
extern "C" {
#endif

// 函数声明(C 和 C++ 都能识别)
int add(int a, int b);
int sub(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;
// c_main.c - C 文件,访问 C++ 中的变量
#include <stdio.h>

// 声明 C++ 中的变量(C 编译器不认识 extern "C",直接声明)
extern int cpp_global;

int main() {
    printf("%d\n", cpp_global); // 输出 100
    cpp_global = 200;           // 可以修改
    printf("%d\n", cpp_global); // 输出 200
    return 0;
}
反向场景:C 中定义变量,让 C++ 代码能访问
// c_var.c - C 文件,定义全局变量
int c_global = 50;
// cpp_main.cpp - C++ 文件,访问 C 中的变量
#include <iostream>
using namespace std;

// extern "C":告诉 C++ 编译器,按 C 规则找这个变量
extern "C" int c_global;

int main() {
    cout << c_global << endl; // 输出 50
    return 0;
}
关键注意
  • extern "C"只能修饰全局变量(局部变量不存在跨文件 / 跨语言访问的问题);
  • const全局变量在 C++ 中默认是'文件内可见',如果想让它被 C 代码访问,需要同时加 extern "C"和extern。
// C++ 文件中定义 const 全局变量,让 C 能访问
extern "C" const int const_global = 30; // const+extern "C"

extern 修饰函数/变量

核心作用

声明'这个函数 / 变量的定义在其他文件里',让当前文件能使用它,这才是'让别的文件能用这个函数'的核心语法,目的是实现多文件编程时的函数共享。

修饰函数

假设你有两个文件:

  1. func.cpp —— 定义函数(实现逻辑)
  2. main.cpp —— 想调用 func.cpp里的函数

错误写法(不写 extern):main.cpp直接调用会报'未定义的引用',因为编译器不知道 add函数的定义在哪里。

正确写法(用 extern 声明):

// 1. func.cpp - 定义函数(实现)
int add(int a, int b) {
    return a + b;
}

// 2. main.cpp - 调用函数(声明后使用)
#include <iostream>
using namespace std;

// extern 声明:告诉编译器,add 函数的定义在其他文件(比如 func.cpp)里
extern int add(int a, int b);

int main() {
    cout << add(3, 5) << endl; // 能正常调用,输出 8
    return 0;
}

不过大部分情况下可以采用头文件的方式解决,因为头文件里声明函数,默认隐含 extern,其他文件包含头文件即可。

// func.h - 头文件(声明函数)
#ifndef FUNC_H
#define FUNC_H

// 函数声明默认带 extern 属性,不用显式写
int add(int a, int b);

#endif // FUNC_H

// func.cpp - 实现函数
#include "func.h"
int add(int a, int b) {
    return a + b;
}

// main.cpp - 调用函数
#include <iostream>
#include "func.h"
using namespace std;

int main() {
    cout << add(3, 5) << endl; // 正常调用
    return 0;
}
extern "C"和extern的结合使用

当你需要:

  1. 让函数能被其他文件使用(extern的作用);
  2. 同时让这个函数按 C 语言规则编译(extern "C"的作用)。

就需要把两者结合,这也是跨语言调用时的标准写法:

// func.h - 兼容 C/C++的头文件
#ifndef FUNC_H
#define FUNC_H

#ifdef __cplusplus
// 1. extern "C":按 C 规则编译;2. 函数声明隐含 extern:让其他文件能用
extern "C" {
#endif

int add(int a, int b); // 声明:让其他文件能调用
int sub(int a, int b);

#ifdef __cplusplus
}
#endif

#endif // FUNC_H

// func.cpp - 实现函数
#include "func.h"

// 这里的 add 会按 extern "C"的规则编译(名字不修饰),同时能被其他文件调用
int add(int a, int b) {
    return a + b;
}

int sub(int a, int b) {
    return a - b;
}
修饰变量
  • 对变量来说,extern的核心作用依然是:声明'这个变量的定义在其他文件里',让当前文件能使用它(和函数的 extern 逻辑完全一致);
  • extern "C"修饰变量的场景极少,核心是让 C++ 中的变量按 C 语言的链接规则处理,解决 C/C++ 互访变量的问题。

extern 修饰变量的核心用法,和函数的 extern 逻辑一致,但要注意变量的'声明'和'定义'必须严格区分:

  • 定义:int num = 10; —— 分配内存、初始化,一个变量只能定义一次;
  • 声明:extern int num; —— 不分配内存,只是告诉编译器'num 在别的文件里定义了',可以声明多次。
场景示例:两个文件共享一个全局变量

假设你有两个文件:

  1. var.cpp —— 定义全局变量(分配内存)
  2. main.cpp —— 想使用这个全局变量
// 1. var.cpp - 定义全局变量(唯一的定义)
#include <iostream>

// 定义:分配内存,初始化值为 10
int global_num = 10;

// 2. main.cpp - 调用全局变量
#include <iostream>
using namespace std;

// extern 声明:告诉编译器,global_num 的定义在其他文件(var.cpp)里
extern int global_num;

int main() {
    cout << global_num << endl; // 输出 10
    global_num = 20;            // 可以修改(因为是普通全局变量)
    cout << global_num << endl; // 输出 20
    return 0;
}
易错点提醒
  • ✅ 正确写法:声明只写 extern int global_num;,定义只在一个文件里写 int global_num = 10;。
  • ❌ 错误写法:extern int global_num = 20; —— 这不是声明,而是重复定义(extern + 初始化 = 定义),编译器会报'重定义'错误。
实际项目写法(头文件声明)

和函数一样,变量也可以在头文件里用 extern 声明,其他文件包含头文件即可:

// var.h - 头文件(声明变量)
#ifndef VAR_H
#define VAR_H

// 声明:仅告诉编译器变量存在,不分配内存
extern int global_num;

#endif // VAR_H

// var.cpp - 定义变量
#include "var.h"
int global_num = 10; // 唯一的定义

// main.cpp - 使用变量
#include <iostream>
#include "var.h"
using namespace std;

int main() {
    cout << global_num << endl; // 输出 10
    return 0;
}

目录

  1. 说到 extern 关键字,一般就想到三种情况:
  2. 我们先来讲 extern "C"
  3. 核心背景
  4. extern "C" 的核心作用
  5. 代码示例:怎么用 extern "C"
  6. 场景 1:C++ 中调用 C 语言写的函数
  7. 场景 2:C++ 中定义能被 C 调用的函数
  8. 场景 3:和函数重载的冲突(重点)
  9. extern "C" 的常见写法
  10. 1. 单个函数:直接在声明 / 定义前加 extern "C"
  11. 2. 多个函数:用 extern "C" { ... }包裹
  12. 3. 兼容头文件(既可以被 C 包含,也可以被 C++ 包含)
  13. extern "C"冷门作用 -- 修饰变量
  14. 场景示例:C++ 中定义变量,让 C 代码能访问
  15. 反向场景:C 中定义变量,让 C++ 代码能访问
  16. 关键注意
  17. extern 修饰函数/变量
  18. 核心作用
  19. 修饰函数
  20. extern "C"和extern的结合使用
  21. 修饰变量
  22. 场景示例:两个文件共享一个全局变量
  23. 易错点提醒
  24. 实际项目写法(头文件声明)

更多推荐文章

查看全部
  • DeepSeek-R1-Distill-Llama-8B 在 Ollama 中的 HTTP API 鉴权与访问控制配置
  • Java 面向对象核心:深入理解 this 关键字
  • SpringBoot 配置文件核心用法(Properties & YAML)
  • Linux 软件源配置实战:CentOS 6.8 环境
  • GitHub 2FA 丢失 Microsoft Authenticator 恢复与重置指南
  • LangChain Agent Skills 实战:构建 GitHub 仓库分析能力
  • Llama-3.2-3B 部署与 Ollama GPU 加速(CUDA/cuDNN)全流程
  • Python 列表与元组的区别及常用操作
  • Ubuntu 与 Windows 系统实现共享文件夹
  • C# 实现文本框仅允许输入数字的键盘事件处理
  • VoxCPM-1.5-TTS-WEB-UI 基于镜像源快速部署指南
  • OpenClaw 接入飞书机器人配置全流程
  • Python 全栈开发与网络安全技术学习路径及实战指南
  • 利用 AI 智能引导快速上手 Quarkus 微服务开发
  • InternLM2 书生·浦语大模型本地化部署指南
  • FPGA AI 加速芯片:从架构设计到边缘部署
  • Java JUC 可重入锁详解:原理与死锁排查
  • Welford 算法:海量数据均值与方差的流式计算
  • MySQL 基础入门指南
  • 【征文计划】玩转 Rokid JSAR:基于 Web 技术栈的 AR 开发环境搭建、核心 API 应用与 3D 时钟等创意项目全流程解析

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online