_export在C语言中如何使用?

99ANYc3cd6
预计阅读时长 13 分钟
位置: 首页 C语言 正文

__export 是什么?

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

c语言_export
(图片来源网络,侵删)

在 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)分别处理,需要维护宏定义,稍显繁琐。

c语言_export
(图片来源网络,侵删)

b) 模块定义文件 (.def) 导出

你可以在一个 .def 文件中列出所有需要导出的函数名和变量名。

// mylib.def
LIBRARY "MyLib"
EXPORTS
    add
    global_counter

编译器会读取这个文件来生成导出表,这种方式比较清晰,但需要额外维护一个 .def 文件。

__export 的作用:简化导出

__export 的出现是为了简化上述过程,当你对一个函数或变量使用 __export 修饰时,编译器会自动完成两件事:

  1. 生成正确的导出指令:就像你在 .def 文件中列出它一样。
  2. 生成修饰名:就像你使用 __declspec(dllexport) 一样。

这意味着,你不需要编写 .def 文件,也不需要使用 __declspec(dllexport) 宏,只需在函数或变量前加上 __export 即可。

c语言_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) 更好?

  1. 更清晰__declspec(dllexport) 的意图非常明确——“这个声明/定义是为了导出”,而 __export 的行为(同时修改 .def 和 .obj)有时会让人困惑。
  2. 更灵活:可以结合宏和条件编译,轻松地在同一个头文件中处理导出和导入场景(如 MYLIB_API 示例)。
  3. 业界标准:这是微软官方推荐的方式,并且被广泛使用。__export 主要是为了兼容旧的代码或特定的构建习惯。

总结与最佳实践

  1. __export 是什么?

    • 一个 MSVC 特有的、非标准的关键字。
    • 用于简化 Windows DLL 的符号导出。
  2. 它做什么?

    • 自动将函数/变量的符号名(包括 C++ 的修饰名)添加到 DLL 的导出表中。
    • 其内部实现方式类似于同时使用 __declspec(dllexport) 和一个 .def 文件。
  3. 如何使用?

    • 直接在函数或变量定义前加 __export
    • __export int my_function();
  4. 现在还应该用吗?

    • 不推荐__export 是一个过时的特性。
  5. 现代最佳实践是什么?

    • 使用 __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 的标准做法。

-- 展开阅读全文 --
头像
dede如何调用文章评论数?
« 上一篇 04-12
dede防注入代码如何有效防御SQL注入攻击?
下一篇 » 04-12

相关文章

取消
微信二维码
支付宝二维码

目录[+]