__export 是什么?
__export 是一个 Microsoft C/C++ 编译器(MSVC)特有的关键字,它的主要作用是告诉编译器,将一个函数或变量的符号名自动添加到 DLL 的导出表中。

在 Windows 中,当你创建一个 DLL 并希望其他程序(如 EXE 或另一个 DLL)能够调用其中的函数或访问其中的变量时,你必须明确地“导出”它们。__export 就是实现这一目的的几种方法之一。
为什么需要 __export?(解决的问题)
要理解 __export 的作用,首先需要了解 Windows DLL 的两种导出方式:
a) 代码导出
这是最传统的方式,在定义函数或变量时使用 __declspec(dllexport)。
// mylib.h #ifdef MYLIB_EXPORTS // 通常在项目设置中定义此宏 #define MYLIB_API __declspec(dllexport) #else #define MYLIB_API __declspec(dllimport) #endif // 导出一个函数 MYLIB_API int add(int a, int b); // 导出一个全局变量 MYLIB_API int global_counter;
使用这种方式,你需要在头文件中为导出和导入(dllimport)分别处理,需要维护宏定义,稍显繁琐。

b) 模块定义文件 (.def) 导出
你可以在一个 .def 文件中列出所有需要导出的函数名和变量名。
// mylib.def
LIBRARY "MyLib"
EXPORTS
add
global_counter
编译器会读取这个文件来生成导出表,这种方式比较清晰,但需要额外维护一个 .def 文件。
__export 的作用:简化导出
__export 的出现是为了简化上述过程,当你对一个函数或变量使用 __export 修饰时,编译器会自动完成两件事:
- 生成正确的导出指令:就像你在
.def文件中列出它一样。 - 生成修饰名:就像你使用
__declspec(dllexport)一样。
这意味着,你不需要编写 .def 文件,也不需要使用 __declspec(dllexport) 宏,只需在函数或变量前加上 __export 即可。

如何使用 __export
使用方法非常简单,直接在函数声明或定义前加上 __export 关键字。
示例:导出函数
// mylib.c
// 使用 __export 修饰函数定义
__export int add(int a, int b) {
return a + b;
}
// 也可以只修饰声明,但通常在定义处修饰更清晰
// __export int add(int a, int b);
示例:导出变量
// mylib.c // 使用 __export 修饰全局变量定义 __export int global_counter = 0;
当编译这个 mylib.c 文件为一个 DLL 项目时,add 函数和 global_counter 变量会被自动添加到 DLL 的导出表中。
__export 的工作原理(重要细节)
__export 的一个核心作用是影响 C++ 的名称修饰(Name Mangling)。
-
对于 C++ 代码:C++ 支持函数重载,因此编译器会为函数名添加额外的信息(如参数类型),以区分不同版本的函数,这被称为“名称修饰”。
void foo(int)和void foo(char)可能会被修饰成?foo@@YAXH@Z和?foo@@YAXD@Z。- 如果你直接在
.def文件中写foo,它可能无法匹配到任何一个修饰后的名称。 - 使用
__export,编译器会自动将修饰后的名称添加到导出表中,确保导出的是正确的符号。
- 如果你直接在
-
对于 C 代码:C 语言不支持函数重载,名称修饰非常简单(或者没有)。
__export在这里的作用主要是确保符号被导出,并确保链接器能找到它。
__export 在 C++ 中尤其有用,因为它能自动处理复杂的名称修饰问题,避免了手动在 .def 文件中输入一长串修饰名的麻烦。
__export vs. __declspec(dllexport)
这是开发者最常混淆的两个概念,它们都能实现导出,但有本质区别:
| 特性 | __export |
__declspec(dllexport) |
|---|---|---|
| 作用 | 同时将符号名添加到 .def 文件和 .obj 文件的导出表中。 | 仅将符号信息添加到 .obj 文件中。 |
| 链接器行为 | 链接器会根据 __export 的指令,在生成 DLL 时自动创建一个临时的 .def 文件来包含所有导出符号。 |
链接器需要额外的信息来知道哪些符号需要导出,这通常通过 .def 文件或 /DEF 链接器选项提供。 |
| 与 C++ 混淆 | 可以。__export 会导出 C++ 的修饰名。 |
不会。__declspec(dllexport) 导出的是 C++ 的修饰名,如果要在 C 中使用,需要 extern "C" 来禁用名称修饰。 |
| 现代用法 | 已过时,不推荐。 | 推荐的标准方式。 |
为什么 __declspec(dllexport) 更好?
- 更清晰:
__declspec(dllexport)的意图非常明确——“这个声明/定义是为了导出”,而__export的行为(同时修改 .def 和 .obj)有时会让人困惑。 - 更灵活:可以结合宏和条件编译,轻松地在同一个头文件中处理导出和导入场景(如
MYLIB_API示例)。 - 业界标准:这是微软官方推荐的方式,并且被广泛使用。
__export主要是为了兼容旧的代码或特定的构建习惯。
总结与最佳实践
-
__export是什么?- 一个 MSVC 特有的、非标准的关键字。
- 用于简化 Windows DLL 的符号导出。
-
它做什么?
- 自动将函数/变量的符号名(包括 C++ 的修饰名)添加到 DLL 的导出表中。
- 其内部实现方式类似于同时使用
__declspec(dllexport)和一个.def文件。
-
如何使用?
- 直接在函数或变量定义前加
__export。 __export int my_function();
- 直接在函数或变量定义前加
-
现在还应该用吗?
- 不推荐。
__export是一个过时的特性。
- 不推荐。
-
现代最佳实践是什么?
- 使用
__declspec(dllexport)配合宏和条件编译。 - 对于 C++ 代码,如果希望 C 代码能调用,请使用
extern "C"。 - 头文件的标准写法如下:
// mylib.h #ifdef _WIN32 #ifdef MYLIB_EXPORTS // 在生成 DLL 的项目宏定义 MYLIB_EXPORTS #define MYLIB_API __declspec(dllexport) #else #define MYLIB_API __declspec(dllimport) // 在使用 DLL 的项目中,不需要定义 MYLIB_EXPORTS #endif #else // 非Windows平台,如Linux/Unix,使用共享库 (.so/.dylib) #define MYLIB_API __attribute__((visibility("default"))) #endif // C++ 代码,希望被 C 调用 extern "C" { MYLIB_API int add(int a, int b); } // 纯 C++ 代码 MYLIB_API std::string get_version(); - 使用
这种写法清晰、可移植性好,并且是当前 Windows C/C++ 开发 DLL 的标准做法。
