Windows和Linux下动态库和静态库的使用
1、文件类型
Tables | Windows | Linux |
---|---|---|
静态库 | .lib | Libxxx.a |
动态库 | .dll与.lib | libxxx.so |
Windows下
一、静态库
在创建win32控制台程序或者win32项目的时候,勾选静态库就可以,进入项目后也可打开工程“属性面板”—》”配置属性”—》”常规”—》配置类型选择静态库。
创建好之后,里面在就正常的写函数或者类,将接口放在.h头文件中,编译后边生成了静态库。
测试代码:
头文件:
源文件:
生成静态库:
Vs中加载
需要文件、接口头文件、.lib库文件。
方式1
步骤1:包头文件。工程“属性面板”—》”配置属性” —》“C/C++” —》” 常规”,在“附加包含目录”属性值中,键入StaticLib.h 头文件所在目录的路径或浏览至该目录。
步骤2:“属性面板”—》”配置属性”—》“链接器”—》”常规”,附加依赖库目录中输入,静态库所在目录;
步骤3:“属性面板”—》”配置属性”—》“链接器”—》”输入”,附加依赖库中输入静态库名StaticLib.lib。
步骤4:使用,引用头文件,即可使用里面的接口。
方式2
步骤1同方式1。
步骤二:
打开工程“属性面板”—》”配置属性” —》“链接器”—》”命令行”,输入静态库的完整路径即可。
方式3:
步骤1同方式1。
步骤2:前提,加载的静态库属于同一解决方案。
工程“属性面板”—》“通用属性”—》 “框架和引用”—》”添加引用”,将显示“添加引用”对话框。 “项目”选项卡列出了当前解决方案中的各个项目以及可以引用的所有库。 在“项目”选项卡中,选择 StaticLib。 单击“确定
方式4: 在文件中使用#pragma comment()指定.lib文件的位置。如图
二、动态库
生成
在创建win32控制台程序或者win32项目的时候,勾选Dll就可以,进入项目后发现自动生成一些文件。如图
我们还是正常定义和实现函数或者类,特别的是动态库生成的时候需要在接口前加上
__declspec(dllexport),而导入的时候需要加__declspec(dllimport)。这里使用通常的宏定义做法,如图所示。
头文件:
源文件:
生成动态库:
这里需要注意,生成两个有用文件,一个是.lib,一个是dll。这里的.lib本质上不同于静态库中的.lib。这里的.lib一般是一些索引信息,记录了dll中函数的入口和位置,dll中是函数的具体实现。而静态库中的lib包含了索引和实现。
加载动态库
加载动态库有两种方式,分为隐式加载和显示加载。
隐式加载
所需文件:接口.h头文件,dll文件,lib文件。
.h和.lib加载方式与静态加载完全一致。但.dll文件必须放在环境变量指定的目录下。当然通常是与目标.exe文件放在一起。
显示加载
所需文件:dll文件。
利用LoadLibrary()函数进行加载。如图
按道理不需要.h头文件,但是前提是你知道接口是啥样的。
隐式加载和显示加载区别很明显,显示加载使用灵活,需要时加载,用完卸载,而隐式加载伴随着整个程序的生命周期。
总结
库分为静态库和动态库。静态库与可执行文件打包,动态库与可执行文件独立。静态库加载需要.lib和.h文件。动态库隐式加载需要.dll、.h、.lib文件,显示加载只需要.dll文件。
————————————————
版权声明:本文为CSDN博主「ShaYX1991」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/a369189453/article/details/81124685
Linux下
动态库的编译与调用
1、创建目录:mkdir dynamic;进入dynamic目录:cd dynamic;新建c文件:vi func.c
2、编辑func.c内容如下,(记得保存)
3、 将func.c编译为动态库,编译命令:
gcc func.c -o func.o -c -fPIC
gcc func.o -shared -o libfunc.so (注其中libfunc.so,lib为前缀,.so是后缀,中间func是库的名字)
4、 在dynamic路径下新建main.c文件:vi main.c;在main.c中调用动态库中的func()函数。
5、 因为fun.c与main.c在同一路径,避免干扰,删除fun.c,func.o文件;命令:rm func.c func.o
6、 编译main.c并调用动态库libfunc.so。命令:gcc main.c -o main -L 库的路径 -l 库名
gcc main.c -o main -L . -l func (点代表当前路径)
7、 执行main,找不到库
8、 需要将动态库拷贝一份到/usr/lib/路径下,命令:sudo cp libfunc.so /usr/lib (需要超级权限);执行:./main
三、静态库的编译与调用
1、创建目录:mkdir static;进入static目录:cd static;新建c文件:vi func.c
2、编辑func.c内容如下,(记得保存)
3、 将func.c编译为静库,编译命令:
gcc func.c -o func.o -c
ar -crv libfunc.a func.o (注lib为前缀,静态库以.a为后缀,中间部分func为库的名字)
4、 在static路径下新建main.c文件:vi main.c;在main.c中调用静态库中的func()函数。
5、 因为fun.c与main.c在同一路径,避免干扰,删除fun.c,func.o文件;命令:rm func.c func.o
6、 编译main.c并调用静态库libfunc.so。命令:gcc main.c -o main -L 库的路径 -l 库名
gcc main.c -o main -L . -l func (点代表当前路径)
7、执行:./main (不需要将静态库拷贝到/usr/lib/)
四、静态编译
我们通常用的是动态编译,比如说调用标准库的printf()函数,printf()的代码不会直接全部的编译到可执行文件,而是相当于在可执行程序中链接调用printf()函数。这样可执行文件的大小就不会很大,但是可执行程序的运行环境必需要含有其所需要的库文件等。静态编译,就是将程序所调用到的代码全部编译到可执行文件,所以编译出来的可执行文件比较大,但是它无需过多依赖运行环境。
比如在桌面创建hello.c;命令:vi hello.c 。编辑内容如图
动态编译:gcc hello.c -o hello_dynamic 静态编译:gcc hello.c -o hello_static -static
————————————————
版权声明:本文为CSDN博主「Genven_Liang」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/nanfeibuyi/article/details/81203021
__declspec(dllexport)与__declspec(dllimport)
区别
他们都是DLL内的关键字,即导出与导入。他们是将DLL内部的类与函数以及数据导出与导入时使用的。
dllexport是在这些类、函数以及数据的申明的时候使用。用他表明这些东西可以被外部函数使用,即(dllexport)是把 DLL中的相关代码(类,函数,数据)暴露出来为其他应用程序使用。使用了(dllexport)关键字,相当于声明了紧接在(dllexport)关键字后面的相关内容是可以为其他程序使用的。
dllimport是在外部程序需要使用DLL内相关内容时使用的关键字。当一个外部程序要使用DLL 内部代码(类,函数,全局变量)时,只需要在程序内部使用(dllimport)关键字声明需要使用的代码就可以了,即(dllimport)关键字是在外部程序需要使用DLL内部相关内容的时候才使用。(dllimport)作用是把DLL中的相关代码插入到应用程序中。
_declspec(dllexport)与_declspec(dllimport)是相互呼应,只有在DLL内部用dllexport作了声明,才能在外部函数中用dllimport导入相关代码。
常见用法
在为方便使用,我们经常在代码中定义宏DLL_EXPORT,此宏用在需要导出的类和函数前,而此宏我们定义如下:
-
#ifdef DLL_EXPORTS
-
#define SIMPLE_CLASS_EXPORT __declspec(dllexport)
-
#else
-
#define SIMPLE_CLASS_EXPORT __declspec(dllimport)
-
#endif
作为动态库,在需要导出的类或函数前必须使用关键字__declspec(dllexport)声明,因此动态库需要定义宏DLL_EXPORTS(使用Vistualstudio建立动态库工程时,此宏已经定义好)。
应用程序需要使用关键字__declspec(dllimport),因此不能定义宏DLL_EXPORTS。
可以省略dllimport???
但MSDN文档里面,对于 __declspec(dllimport)的说明让人感觉有点奇怪,先来看看MSDN里面是怎么说的:
不使用 __declspec(dllimport)也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。
使用__declspec(dllimport)可以生成更好的代码,这点好理解,但必须使用它才能导出dll中的变量,真的是如此吗?那我们就来测试一下:
建立动态库,由SimpleClass.h和SimpleClass.cpp组成,SimpleClass.h的代码实现如下:
-
//file SimpleClass.h
-
#ifndef _SIMPLE_CLASS_H_
-
#define _SIMPLE_CLASS_H_
-
#ifdef DLL_EXPORTS
-
#define SIMPLE_CLASS_EXPORT__declspec(dllexport)
-
#else
-
#define SIMPLE_CLASS_EXPORT
-
#endif
-
extern int SIMPLE_CLASS_EXPORT g_Vaule; //全局变量
-
class SIMPLE_CLASS_EXPORT CSimpleClass
-
{
-
public:
-
CSimpleClass(void);
-
~CSimpleClass(void);
-
int GetVale(void)const;
-
};
-
#endif
SimpleClass.cpp代码实现如下:
-
//SimpleClass.cpp
-
#include "SimpleClass.h"
-
int g_Vaule = 100;
-
CSimpleClass::CSimpleClass(void)
-
:m_iValue(100)
-
{}
-
CSimpleClass::~CSimpleClass(void)
-
{}
-
int CSimpleClass::GetVale(void)const
-
{
-
return g_Vaule;
-
}
如果应用程序中不直接使用g_Vaule,能顺利编译通过,且调用函数GetVale能正确返回100.
但如果在应用程序中直接使用g_Vaule,编译错误提示如下:
1>main.obj : error LNK2001: unresolvedexternal symbol "int g_Vaule" ([email protected]@3HA)
如果将SimpleClass.h中的宏定义修改为下面值,成功编译:
-
#ifdef DLL_EXPORTS
-
#define SIMPLE_CLASS_EXPORT __declspec(dllexport)
-
#else
-
#define SIMPLE_CLASS_EXPORT __declspec(dllimport)
-
#endif
总结如下:对于动态库本身必须使用关键字__declspec(dllexport),对于应用程序,如果不使用动态库导出的变量,不使用关键字__declspec(dllimport)也可以保证动态库的正常使用,但实际使用中,还是建议应用程序使用关键字__declspec(dllimport),具体原因,还是上面MSDN的那段话。
动态库与静态库并存
另外,有时我们的程序需要同时提供动态库和静态库库,且都使用一个头文件,为了解决关键字的使用冲突,建议使用如下的宏定义:
-
#ifdefined DLL_EXPORTS
-
#ifdefined INSIDE_DLL
-
#define SIMPLE_CLASS_EXPORT__declspec(dllexport)
-
#else
-
#define SIMPLE_CLASS_EXPORT__declspec(dllimport)
-
#endif
-
#else
-
#define SIMPLE_CLASS_EXPORT
-
#endif
对于动态库本身,需要定义宏DLL_EXPORTS和INSIDE_DLL 使用动态库的应用程序定义宏DLL_EXPORTS
对于静态库,不需要定义DLL_EXPORTS,当然静态库的应用程序也不需要定义。
如此定义,就可以让动态库和静态库的导出都使用同一份头文件。