Windows和Linux下动态库和静态库的使用
发布日期:2021-11-15 14:57:50 浏览次数:39 分类:技术文章

本文共 5797 字,大约阅读时间需要 19 分钟。

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,此宏用在需要导出的类和函数前,而此宏我们定义如下:

 
  1. #ifdef DLL_EXPORTS

  2.  
  3. #define SIMPLE_CLASS_EXPORT __declspec(dllexport)

  4.  
  5. #else

  6.  
  7. #define SIMPLE_CLASS_EXPORT __declspec(dllimport)

  8.  
  9. #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的代码实现如下:

 
  1. //file SimpleClass.h

  2. #ifndef _SIMPLE_CLASS_H_

  3. #define _SIMPLE_CLASS_H_

  4.  
  5. #ifdef DLL_EXPORTS

  6. #define SIMPLE_CLASS_EXPORT__declspec(dllexport)

  7. #else

  8. #define SIMPLE_CLASS_EXPORT

  9. #endif

  10.  
  11. extern int SIMPLE_CLASS_EXPORT g_Vaule; //全局变量

  12.  
  13. class SIMPLE_CLASS_EXPORT CSimpleClass

  14. {

  15. public:

  16. CSimpleClass(void);

  17. ~CSimpleClass(void);

  18.  int GetVale(void)const;

  19. };

  20. #endif

SimpleClass.cpp代码实现如下:

 
  1. //SimpleClass.cpp

  2.  
  3. #include "SimpleClass.h"

  4.  
  5. int g_Vaule = 100;

  6.  
  7. CSimpleClass::CSimpleClass(void)

  8. :m_iValue(100)

  9. {}

  10. CSimpleClass::~CSimpleClass(void)

  11. {}

  12. int CSimpleClass::GetVale(void)const

  13. {

  14. return g_Vaule;

  15. }

如果应用程序中不直接使用g_Vaule,能顺利编译通过,且调用函数GetVale能正确返回100.

    但如果在应用程序中直接使用g_Vaule,编译错误提示如下:

1>main.obj : error LNK2001: unresolvedexternal symbol "int g_Vaule" (?g_Vaule@@3HA)

   

    如果将SimpleClass.h中的宏定义修改为下面值,成功编译:

 
  1. #ifdef DLL_EXPORTS

  2. #define SIMPLE_CLASS_EXPORT __declspec(dllexport)

  3. #else

  4. #define SIMPLE_CLASS_EXPORT __declspec(dllimport)

  5. #endif

总结如下:对于动态库本身必须使用关键字__declspec(dllexport),对于应用程序,如果不使用动态库导出的变量,不使用关键字__declspec(dllimport)也可以保证动态库的正常使用,但实际使用中,还是建议应用程序使用关键字__declspec(dllimport),具体原因,还是上面MSDN的那段话。

 动态库与静态库并存

    另外,有时我们的程序需要同时提供动态库和静态库库,且都使用一个头文件,为了解决关键字的使用冲突,建议使用如下的宏定义:

 
  1. #ifdefined DLL_EXPORTS

  2. #ifdefined INSIDE_DLL

  3. #define SIMPLE_CLASS_EXPORT__declspec(dllexport)

  4. #else

  5. #define SIMPLE_CLASS_EXPORT__declspec(dllimport)

  6. #endif

  7. #else

  8. #define SIMPLE_CLASS_EXPORT

  9. #endif

对于动态库本身,需要定义宏DLL_EXPORTS和INSIDE_DLL 使用动态库的应用程序定义宏DLL_EXPORTS

对于静态库,不需要定义DLL_EXPORTS,当然静态库的应用程序也不需要定义。

如此定义,就可以让动态库和静态库的导出都使用同一份头文件。

转载地址:https://blog.csdn.net/qq_39742146/article/details/102656831 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Qt中使用Qss修改边框宽度,字体颜色,修改选择时背景色和字体颜色
下一篇:vs2015上安装小番茄插件

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年03月25日 02时00分43秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

k8s边缘节点_边缘计算容器及K8S应用的三种场景及部署选项 2019-04-21
诸葛io的技术架构图_基于泳道技术生成“无数”个测试环境 2019-04-21
互相引用 spring_# 技术笔记:spring、springBoot源码解析 2019-04-21
华为发布岳云鹏手机_鸿蒙2.0正式发布:明年华为手机全面升级 2019-04-21
ifpc挖filecoin_Filecoin挖矿分析全套 不容错过的干货 2019-04-21
python扫雷 高级算法_Python玩转算法—扫雷 2019-04-21
牛客网python测试考试答案_牛客网SQL题库之考试分数 2019-04-21
git获取所有branch_使用jGit 通过gitUrl 获取Git的所有分支 2019-04-21
mysql like 数字结尾_重拾MySQL之正则表达式 2019-04-21
mysql where从句_《快速念咒——MySQL自学入门指南》:第1章第8节:模糊查询LIKE——一窝兔子(上)... 2019-04-21
mysql 重置密码_mysql忘记密码如何重置密码,以及修改root密码的三种方法 2019-04-21
python-docx tables后追加内容_Mac brew安装MySQL8.0.21后忘记密码(重置密码篇) 2019-04-21
python中两个时间相减结果转为小时_Python起步(二)基础数据类型1 2019-04-21
定义泛化。举个例子_网易考拉应用的dubbo泛化调用,是如何实现的? 2019-04-21
mysql里可以用cube吗_sql server的cube操作符使用详解_mysql 2019-04-21
php mysql 图书_使用PHP+MySQL来对图书管理系统进行构建 2019-04-21
单片机c语言 int1,51单片机into、int1中断计数c语言源程序.doc 2019-04-21
c语言课程设计工资管理建库,C语言课程设计工资管理系统参考.doc 2019-04-21
c语言case中途跳出,break语句在switch结构语句中的作用是终止某个case,并跳出switch结构语句。... 2019-04-21
c51写c语言外部ram头文件,C51中访问外部RAM的方法 2019-04-21