export 不是标准 C 语言关键字
最重要的一点是:export 不是标准 C 语言(C89/C90, C99, C11, C17, C23)的一部分。

这意味着,如果你在遵循标准 C 语言的编译器(如 GCC、Clang 在标准模式下)中写下 export int var;,编译器会直接报错,提示 error: expected '=', ',', ';', 'asm' or '__attribute__' before 'export'。
你为什么会看到 export 呢?它主要出现在两个特定的上下文中:
- C++ 语言中的模块
- Windows 平台下的 DLL(动态链接库)开发
下面我们分别对这两种情况进行详细解释。
C++ 中的 export (已废弃)
在 C++ 语言中,export 关键字曾经被用于“模块化编程”(Exported Templates)。

-
目的:在 C++98/03 标准,模板的声明和定义必须放在同一个头文件中,这会导致编译速度慢,并且容易引发多重包含的问题。
export的目的是允许将模板的声明放在头文件(.h),而将模板的定义放在源文件(.cpp)中,从而实现分离编译。 -
语法:
// my_template.h #pragma once export template<typename T> class MyClass { public: void foo(); }; // my_template.cpp #include "my_template.h" template<typename T> void MyClass<T>::foo() { // ... } -
现状:
export的实现非常复杂,几乎没有主流编译器(包括 GCC 和 MSVC)在 C++11 标准之前完全支持它,由于其实现困难且使用率极低,export在 C++17 标准中被正式废弃和移除。
如果你现在正在学习 C++,可以完全忽略 export,它已经是一个历史产物。

Windows DLL 中的 export (最常见的情况)
在 Windows 平台下开发 DLL(Dynamic-Link Library,动态链接库)时,export 关键字被广泛使用,但它并不是一个真正的 C 语言关键字,而是 Microsoft C/C++ 编译器 (cl.exe) 的一个扩展。
它的作用是:明确告诉编译器,将某个函数或变量导出,使其可以被外部的可执行文件(.exe)或其他 DLL 调用。
为什么需要 export?
Windows 的 DLL 机制需要一个“清单”,告诉系统哪些函数是“对外公开”的,这个清单通常通过两种方式生成:
.def文件:一个单独的文本文件,列出所有导出的函数名。__declspec(dllexport):在代码中直接使用编译器提供的指令来声明。
export 关键字是 __declspec(dllexport) 的一个语法糖,或者说是一种更简洁的写法。
export 与 __declspec(dllexport) 的关系
在 MSVC 编译器中,export 和 __declspec(dllexport) 在功能上是等价的。
// 这两种写法在 MSVC 中效果完全相同 __declspec(dllexport) int my_function(); export int my_function();
实际使用场景
一个典型的 DLL 项目会使用 条件编译 来区分“编译 DLL 本身”和“使用 DLL”两种情况。
DLL 源文件 (mylib.c)
// 定义一个宏,用于标记导出
#ifdef MYLIB_EXPORTS
#define MYLIB_API __declspec(dllexport) // 或者使用 export
#else
#define MYLIB_API __declspec(dllimport) // 对于使用者来说是导入
#endif
// 使用这个宏来声明函数
MYLIB_API int add(int a, int b);
// 普通函数,不导出,只能在 DLL 内部使用
static void internal_helper() {
// ...
}
int add(int a, int b) {
internal_helper();
return a + b;
}
MYLIB_EXPORTS:这个宏通常在项目属性中定义,当编译mylib.dll时,我们定义它,这时MYLIB_API被展开为__declspec(dllexport),表示“我要把这个函数导出”。__declspec(dllimport):当其他项目(如一个 .exe 文件)想要使用这个 DLL 中的add函数时,它们会包含mylib.h,并且不会定义MYLIB_EXPORTS,这时MYLIB_API被展开为__declspec(dllimport),这会告诉编译器:“这个函数在 DLL 里,调用时需要生成特殊的间接寻址代码”,这比普通函数调用效率更高。
DLL 的头文件 (mylib.h)
// mylib.h
#ifndef MYLIB_H
#define MYLIB_H
#ifdef MYLIB_EXPORTS
#define MYLIB_API __declspec(dllexport) // 或者 export
#else
#define MYLIB_API __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C" {
#endif
MYLIB_API int add(int a, int b);
#ifdef __cplusplus
}
#endif
#endif // MYLIB_H
extern "C":这是一个非常重要的部分,它告诉 C++ 编译器,使用 C 语言的链接规则来处理这些函数,这可以解决 C++ 的名称修饰(Name Mangling)问题,确保生成的函数名在 C 和 C++ 中是兼容的,使得 C 代码可以顺利调用 C++ 编译的 DLL 中的函数。
使用 DLL 的程序 (main.c)
// main.c
#include <stdio.h>
#include "mylib.h" // 包含头文件
// 注意:在使用 DLL 的项目中,不要定义 MYLIB_EXPORTS 宏
int main() {
int result = add(5, 3);
printf("Result from DLL: %d\n", result); // 输出: Result from DLL: 8
return 0;
}
现代 Windows 开发:__declspec(dllexport) vs. .def 文件
-
__declspec(dllexport):- 优点:直接写在代码里,方便,与代码紧密耦合。
- 缺点:如果函数签名改变(例如参数类型),导出名可能会因名称修饰而改变,导致不兼容。
-
.def文件:- 优点:明确指定导出的函数名(可以指定为 unmangled 名称,如
extern "C"的函数),更稳定,不受编译器版本影响。 - 缺点:需要维护一个额外的文件。
- 优点:明确指定导出的函数名(可以指定为 unmangled 名称,如
在大型项目中,.def 文件更受青睐,但在中小型或快速开发的项目中,__declspec(dllexport) 非常流行。
| 场景 | 关键字 | 解释 | 适用性 |
|---|---|---|---|
| 标准 C 语言 | export |
不是标准关键字,直接使用会编译错误。 | 不适用 |
| C++ 语言 | export |
曾经用于模板分离编译,但已在 C++17 中被废弃和移除。 | 已废弃,不使用 |
| Windows DLL | export |
MSVC 编译器扩展,是 __declspec(dllexport) 的同义词,用于导出函数/变量。 |
仅适用于 MSVC 编译器 |
| 跨平台 DLL | __attribute__((visibility("default"))) |
GCC/Clang 中的等效宏,用于实现类似功能。 | 适用于 GCC/Clang |
最终建议:
- 如果你写的是标准 C/C++ 代码,请忘记
export这个词的存在。 - 如果你正在为 Windows 开发 DLL,并且使用 MSVC 编译器,
export是一个可选项,但更常见和推荐的是使用__declspec(dllexport)结合条件编译宏。 - 如果你需要编写跨平台的 DLL 代码,可以使用预处理器来区分平台:
#ifdef _MSC_VER #define API_EXPORT __declspec(dllexport) #elif defined(__GNUC__) || defined(__clang__) #define API_EXPORT __attribute__((visibility("default"))) #else #error "Unsupported compiler" #endif
