msvc的__declspec(dllexport)
一、__declspec(dllexport)的含义
_declspec(dllexport)
是 Microsoft 特定的一个编译器扩展,用于指示编译器将符号导出到动态链接库(DLL)中。它并不是一个标准 C++ 关键字或语法,而是 __declspec
关键字的一个特定用法。__declspec
是 "declare specifier" 的缩写,表示声明说明符。dllexport
则是 "DLL export" 的缩写,表示将符号导出到 DLL 中。
因此,__declspec(dllexport)
的含义可以分解为:
__declspec
:声明说明符,用于指定某些属性或行为。dllexport
:DLL 输出,表示将符号导出到 DLL 中。
使用场景
-
创建 DLL: 当您创建一个 DLL 并希望某些函数或变量可以被外部模块调用时,您可以使用
__declspec(dllexport)
来导出这些符号。 -
声明导出的符号: 使用
__declspec(dllexport)
声明的函数或变量将在编译时生成相应的导出信息,使得这些符号可以被其他模块识别和使用。
二、__declspec(dllexport)具体指示构建工具链中哪些程序的哪些行为
__declspec(dllexport)`的作用主要体现在编译和链接阶段,具体指示构建工具链中编译器和链接器的行为。
编译器行为(如 cl.exe)
1. 符号导出:
- 当编译器遇到使用 __declspec(dllexport)声明的符号(例如函数或变量)时,它会将这些符号标记为导出符号。这意味着这些符号将在生成的对象文件中被标记为需要导出。
- 编译器会在对象文件的符号表中添加相应的导出信息,使得这些符号在链接阶段可以被识别为导出符号。
链接器行为(如 link.exe)
1. 创建导出表:
- 链接器在生成 DLL 时,会根据对象文件中的导出符号信息创建导出表。导出表包含所有被标记为导出的符号,使得这些符号可以被其他模块识别和使用。
- 导出表是 DLL 文件的一部分,包含符号的名称、地址等信息。
2. 生成导出库文件:
- 链接器还会生成一个导出库文件(`.lib` 文件),该文件包含导出符号的引用信息。这个库文件用于在编译和链接使用该 DLL 的应用程序时提供符号解析。
- 应用程序在链接阶段使用这个导出库文件来解析对 DLL 中导出符号的引用。
### 示例
假设我们有以下代码:
cpp
// MyLibrary.h #ifndef MYLIBRARY_H #define MYLIBRARY_H #ifdef MYLIBRARY_EXPORTS #define MYLIBRARY_API __declspec(dllexport) #else #define MYLIBRARY_API __declspec(dllimport) #endif MYLIBRARY_API void MyFunction(); #endif // MYLIBRARY_H
cpp
// MyLibrary.cpp #include "MyLibrary.h" void MyFunction() { // Function implementation }
构建过程:
1. 编译阶段:
- 使用 cl.exe 编译 MyLibrary.cpp:
sh
cl /c /DMYLIBRARY_EXPORTS MyLibrary.cpp
- 编译器将 MyFunction 标记为导出符号,并在生成的对象文件 MyLibrary.obj 中包含相应的导出信息。
2. 链接阶段:
- 使用 link.exe 将对象文件链接为 DLL:
sh
link /DLL /OUT:MyLibrary.dll MyLibrary.obj
- 链接器根据对象文件中的导出符号信息创建导出表,并将其包含在生成的 `MyLibrary.dll` 中。
- 链接器还生成一个导出库文件 `MyLibrary.lib`,用于在其他模块中链接和使用该 DLL。
总结
__declspec(dllexport)` 主要影响编译器和链接器的行为:
编译器:标记符号为导出符号,并在对象文件中包含相应的导出信息。
链接器:创建导出表并生成导出库文件,使得这些符号可以被其他模块识别和使用。
三、g++类似的行为
在 Linux 下使用 g++ 构建共享库(Shared Library)时,虽然没有直接等价于 __declspec(dllexport)的关键字,但可以通过其他方法实现类似的导出符号功能。主要有以下几种方式:
1. 使用 __attribute__((visibility("default")))
GCC 提供了 __attribute__机制,可以用来控制符号的可见性。__attribute__((visibility("default")))类似于 __declspec(dllexport),用于将符号导出到共享库中。
示例:
cpp
// MyLibrary.h #ifndef MYLIBRARY_H #define MYLIBRARY_H #if defined(__GNUC__) && __GNUC__ >= 4 #define MYLIBRARY_API __attribute__((visibility("default"))) #else #define MYLIBRARY_API #endif MYLIBRARY_API void MyFunction(); #endif // MYLIBRARY_H
cpp
// MyLibrary.cpp #include "MyLibrary.h" void MyFunction() { // Function implementation }
构建共享库:
sh
g++ -fPIC -shared -o libMyLibrary.so MyLibrary.cpp
2. 使用版本脚本(Version Script)
版本脚本是一种更为灵活的方法,可以精确控制哪些符号被导出。通过在链接阶段指定版本脚本,可以明确指定哪些符号应该被导出。
示例:
创建版本脚本 `MyLibrary.map`:
{ global: MyFunction; local: };
构建共享库:
sh
g++ -fPIC -shared -o libMyLibrary.so MyLibrary.cpp -Wl,--version-script=MyLibrary.map
3. 默认导出所有符号
在构建共享库时,可以通过编译器选项导出所有符号。不过,这种方法不推荐用于大型项目,因为它会导出所有符号,可能导致命名冲突。
构建共享库:
sh
g++ -fPIC -shared -o libMyLibrary.so MyLibrary.cpp
总结
在 Linux 下使用 g++构建共享库时,可以通过以下几种方法实现导出符号的功能:
1. __attribute__((visibility("default"))):类似于 __declspec(dllexport),用于导出特定符号。
2. 版本脚本:通过版本脚本精确控制导出符号。
3. 默认导出所有符号:不推荐,但可以通过编译器选项导出所有符号。
这些方法可以根据具体需求选择使用,以便在 Linux 下实现与 __declspec(dllexport)`类似的功能。