C++ 静态库与动态库以及在 Windows上 的创建、使用
一、什么是库
库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a 或 .lib)和动态库(.so 或 .dll)。
所谓静态、动态是指链接。回顾一下,将一个程序编译成可执行程序的步骤:
二、什么是静态库
之所以成为【静态库】,是因为在链接阶段,会将汇编生成的目标文件 .o 与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟 .o 文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj 文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结:
- 静态库对函数库的链接是放在编译时期完成的。
- 程序在运行时与函数库再无瓜葛,移植方便。
- 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
下面编写一些简单的四则运算 C++ 类,将其编译成静态库给他人用,头文件如下所示:
#pragma once
class StaticMath
{
public:
StaticMath(void);
~StaticMath(void);
static double add(double a, double b);//加法
static double sub(double a, double b);//减法
static double mul(double a, double b);//乘法
static double div(double a, double b);//除法
void print();
};
包含pragma once
语句的文件只会被编译一次,和头文件中用#ifndef... #define... #endif
的效果类似。
三、Windows 下创建与使用静态库
3.1 创建静态库
如果是使用 VS 命令行生成静态库,也是分两个步骤来生成程序:
- 首先,通过使用带编译器选项 /c 的 Cl.exe 编译代码 (cl /c StaticMath.cpp),创建名为 “StaticMath.obj” 的目标文件。
- 然后,使用库管理器 Lib.exe 链接代码 (lib StaticMath.obj),创建静态库 StaticMath.lib。
当然,我们一般不这么用,使用 VS 工程设置更方便。创建 Win32 控制台程序时,勾选静态库类型;打开工程 “属性面板” → ”配置属性” → ”常规”,配置类型选择静态库。
编译项目即可生成静态库。
3.2 使用静态库
创建 Win32 控制台测试程序:
#include "StaticMath.h"
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
double a = 10;
double b = 2;
cout << "a + b = " << StaticMath::add(a, b) << endl;
cout << "a - b = " << StaticMath::sub(a, b) << endl;
cout << "a * b = " << StaticMath::mul(a, b) << endl;
cout << "a / b = " << StaticMath::div(a, b) << endl;
StaticMath sm;
sm.print();
system("pause");
return 0;
}
有 3 种使用方法:
方法一:
这也是最常用的方法:
- “属性面板” → ”配置属性” → “链接器” → ”常规”,附加依赖库目录中输入,静态库所在目录;
- “属性面板” → ”配置属性” → “链接器” → ”输入”,附加依赖库中输入静态库名 StaticLibrary.lib。
编译运行 OK。
方法二:
打开工程 “属性面板” → ”配置属性” → “链接器” → ”命令行”,输入静态库的完整路径即可。
方法三:
工程 “属性面板” → “通用属性 ”→ “框架和引用” → ”添加引用”,将显示 “添加引用” 对话框。 “项目” 选项卡列出了当前解决方案中的各个项目以及可以引用的所有库。 在 “项目” 选项卡中,选择 StaticLibrary,单击 “确定”。
添加 StaticMath.h 头文件目录,必须修改包含目录路径。打开工程 “属性面板” → ”配置属性” → “C/C++” → ” 常规”,在 “附加包含目录” 属性值中,键入 StaticMath.h 头文件所在目录的路径或浏览至该目录。
四、什么是动态库
通过上面的介绍发现静态库,容易使用和理解,也达到了代码复用的目的,那为什么还需要动态库呢?
为什么需要动态库,其实也是静态库的特点导致。
空间浪费是静态库的一个问题。
另一个问题是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库 liba.lib 更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
动态库特点总结:
- 动态库把对一些库函数的链接载入推迟到程序运行的时期。
- 可以实现进程之间的资源共享。(因此动态库也称为共享库)
- 将一些程序升级变得简单。
- 甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。
Window 与 Linux 执行文件格式不同,在创建动态库的时候有一些差异。
- 在 Windows 系统下的执行文件格式是 PE 格式,动态库需要一个 DllMain 函数做出初始化的入口,通常在导出函数的声明时需要有 _declspec(dllexport)关 键字。
- Linux 下 gcc 编译的执行文件默认是 ELF 格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。
五、Windows 下创建与使用动态库
5.1 创建动态库
与 Linux 相比,在 Windows 系统下创建动态库要稍微麻烦一些。首先,需要一个 DllMain 函数做出初始化的入口(创建 win32 控制台程序时,勾选 DLL 类型会自动生成这个文件):
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
通常在导出函数的声明时需要有 _declspec(dllexport) 关键字:
#pragma once
class DynamicMath
{
public:
__declspec(dllexport) DynamicMath(void);
__declspec(dllexport) ~DynamicMath(void);
static __declspec(dllexport) double add(double a, double b);//加法
static __declspec(dllexport) double sub(double a, double b);//减法
static __declspec(dllexport) double mul(double a, double b);//乘法
static __declspec(dllexport) double div(double a, double b);//除法
__declspec(dllexport) void print();
};
生成动态库需要设置工程属性,打开工程 “属性面板” → ”配置属性” → ”常规”,配置类型选择动态库。
编译项目即可生成动态库。
5.2 使用动态库
创建 Win32 控制台测试程序:
#include "stdafx.h"
#include "DynamicMath.h"
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
double a = 10;
double b = 2;
cout << "a + b = " << DynamicMath::add(a, b) << endl;
cout << "a - b = " << DynamicMath::sub(a, b) << endl;
cout << "a * b = " << DynamicMath::mul(a, b) << endl;
cout << "a / b = " << DynamicMath::div(a, b) << endl;
DynamicMath dyn;
dyn.print();
system("pause");
return 0;
}
有 2 种使用方法:
方法一:
“属性面板” → ”配置属性” → “链接器” → ”常规”,附加依赖库目录中输入,动态库所在目录;
“属性面板” → ”配置属性” → “链接器” → ”输入”,附加依赖库中输入动态库编译出来的 DynamicLibrary.lib。
编译运行 OK。
这里可能大家有个疑问,动态库怎么还有一个 DynamicLibrary.lib 文件?即无论是静态链接库还是动态链接库,最后都有 lib 文件,那么两者区别是什么呢?其实,两个是完全不一样的东西。
StaticLibrary.lib 的大小为 190KB,DynamicLibrary.lib 的大小为 3KB,静态库对应的 lib 文件叫静态库,动态库对应的lib文件叫【导入库】。实际上静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。
方法二:
工程 “属性面板” → “通用属性” → “框架和引用” → ”添加引用”,将显示 “添加引用” 对话框。“项目” 选项卡列出了当前解决方案中的各个项目以及可以引用的所有库。 在 “项目” 选项卡中,选择 DynamicLibrary,单击 “确定”。
添加 DynamicMath.h 头文件目录,必须修改包含目录路径。打开工程 “属性面板” → ”配置属性” → “C/C++” → ” 常规”,在 “附加包含目录” 属性值中,键入 DynamicMath.h 头文件所在目录的路径或浏览至该目录。
六、在 Windows 下显式调用动态库
应用程序必须进行函数调用以在运行时显式加载 DLL。为显式链接到 DLL,应用程序必须:
- 调用 LoadLibrary(或相似的函数)以加载 DLL 和获取模块句柄。
- 调用 GetProcAddress,以获取指向应用程序要调用的每个导出函数的函数指针。由于应用程序是通过指针调用 DLL 的函数,编译器不生成外部引用,故无需与导入库链接。
- 使用完 DLL 后调用 FreeLibrary。
对 C++ 来说,情况稍微复杂。显式加载一个 C++ 动态库的困难一部分是因为 C++ 的 name mangling;另一部分是因为没有提供一个合适的 API 来装载类,在 C++ 中,您可能要用到库中的一个类,而这需要创建该类的一个实例,这不容易做到。
name mangling 可以通过 extern “C” 解决。C++ 有个特定的关键字用来声明采用 C binding 的函数:extern “C” 。用 extern “C” 声明的函数将使用函数名作符号名,就像 C 函数一样。因此,只有非成员函数才能被声明为 extern “C”,并且不能被重载。尽管限制多多,extern “C” 函数还是非常有用,因为它们可以象 C 函数一样被 dlopen 动态加载。冠以 extern “C” 限定符后,并不意味着函数中无法使用 C++ 代码了,相反,它仍然是一个完全的 C++ 函数,可以使用任何 C++ 特性和各种类型的参数。
另外如何从 C++ 动态库中获取类,附上几篇相关文章,但我并不建议这么做:
- 《LoadLibrary调用DLL中的Class》:http://www.cppblog.com/codejie/archive/2009/09/24/97141.html
- 《C++ dlopen mini HOWTO》:http://blog.csdn.net/denny_233/article/details/7255673
“显式” 使用 C++ 动态库中的 Class 是非常繁琐和危险的事情,因此能用 “隐式” 就不要用 “显式”,能静态就不要用动态。
七、总结
二者的不同点在于代码被载入的时刻不同。
- 静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大。
- 动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小。
静态编译:编译器在编译可执行文件的时候,将可执行文件需要调用的对应静态库(.a 或 .lib)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。
动态编译:可执行文件需要附带一个的动态链接库(.so 或 .dll),在执行时,需要调用其对应动态链接库中的命令。
- 优点:一方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源。
- 缺点:一是哪怕是很简单的程序,只用到了链接库中的一两条命令,也需要附带一个相对庞大的链接库;二是如果其他计算机上没有安装对应的运行库,则用动态编译的可执行文件就不能运行。
这里暂时只介绍 Windows 下库的创建和使用,Linux 暂不涉及,以后有空再总结。
参考:
【C/C++开发】C++静态库与动态库以及在Linux和Windows上的创建使用
C++ 静态库与动态库以及在 Windows上 的创建、使用的更多相关文章
- C++ 系列:静态库与动态库
转载自http://www.cnblogs.com/skynet/p/3372855.html 这次分享的宗旨是——让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择 ...
- C++静态库与动态库
C++静态库与动态库 这次分享的宗旨是--让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择.这里不深入介绍静态库.动态库的底层格式,内存布局等,有兴趣的同学,推荐一 ...
- Linux下Gcc生成和使用静态库和动态库详解(转)
一.基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同( ...
- Makefile中静态库,动态库的创建和使用以及解压缩命令
应用层通过操作文件操控硬件 使用制作好的工具链: 刚开始学习时,用一些已经制作好的工具链,使用以下命令解压到gcc-3.4.5-glibc-2.3.6目录 cd /work/tools tar xjf ...
- VS中Debug和Realease、及静态库和动态库的区别整理(转)
原文出自:http://www.cnblogs.com/chensu/p/5632486.html 一.Debug和Realease区别产生的原因 Debug 通常称为调试版本,它包含调试信息,并且不 ...
- Linux下C++静态库、动态库的制作与使用
参考博文:C++静态库与动态库 >> 静态库 1. 静态库的制作 a) 编辑 name.cpp 和name.h文件 b) $g++ -c name.cpp //注意带参数-c,否则直接编译 ...
- 在Linux下如何使用GCC编译程序、简单生成 静态库及动态库
最近在编写的一个Apache kafka 的C/C++客户端,,在看他写的 example中,他的编译是用librdkafka++.a和librdkafka.a 静态库编译的,,,而我们这 ...
- VS中Debug和Realease、及静态库和动态库的区别整理
一.Debug和Realease区别产生的原因 Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序.Release 称为发布版本,它往往是进行了各种优化,使得程序在代码 ...
- Linux下Gcc生成和使用静态库和动态库详解
参考文章:http://blog.chinaunix.net/uid-23592843-id-223539.html 一.基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库 ...
- android开发 NDK 编译和使用静态库、动态库 (转)
在eclipse工程目录下建立一个jni的文件夹 在jni文件夹中建立Android.mk和Application.mk文件 Android.mk文件: Android提供的一种makefile文件, ...
随机推荐
- CF 704 D. Captain America
CF 704 D. Captain America 题目链接 题目大意:给出\(n\)个点的坐标,你要将每个点染成红色或者蓝色.染一个红色要付出\(r\)的代价,染一个蓝色要付出\(b\)的代价.有\ ...
- IT兄弟连 Java语法教程 算符运算符
Java提供了丰富的运算符环境.可以将大部分Java运算符划分为4组:算术运算符.位运算符.关系运算符以及逻辑运算符.Java还定义了一些用于处理某些特定情况的附加运算符.本章将介绍除类型比较运算符i ...
- 前端笔记之React(一)初识React&组件&JSX语法
一.React项目起步配置 官网:https://reactjs.org/ 文档:https://reactjs.org/docs/hello-world.html 中文:http://react.c ...
- WildFly16.0配置数据源并测试
目前网络上罕有近期版本的配置,本人经多次尝试网络中不同方式配置,在Testing Connection一步中尽皆失败后,查询官方文档未果,摸索出一条可行之路,在此分享于诸位朋友. [../wildfl ...
- CAT 监控搭建
简介 CAT 是基于 Java 开发的实时应用监控平台,为美团点评提供了全面的实时监控告警服务. 已经在美团点评的基础架构中间件框架(MVC框架,RPC框架,数据库框架,缓存框架等,消息队列,配置系统 ...
- wpf file embeded resource is readonly,Copy always will copy the file and its folder to the bin folder
Wpf file embeded resource will compile the file into the assembly and it will be readonly and can no ...
- PHP中设计模式以及魔术方法
1.设计模式 1.1单例模式 口诀:三私一公 1.私有的静态属性用来保存对象的单例 2.私有的构造方法用来阻止在类的外部实例化 3.私有的__clone阻止在类的外部clo ...
- 服务端性能测试技能tree
ALL: Left: Right: 摘抄一下(觉得不错) 以下来自百度百科 ---- 软件性能测试 软件性能测试是在交替进行负荷和强迫测试时常用的术语.理想的“软件性能测试”(和其他类型的测试)应在需 ...
- 干货:Wireshark使用技巧-显示规则
- 显示规则使用 在Wireshark界面对已经抓取的报文在界面的显示进行控制的规则,称为显示规则,显示规则只是让一部分不符合规则的报文不被显示,但未被丢弃,这些报文仍然存在在Wireshark的系统 ...
- Swift相比OC语言有哪些优点
Swift相比OC语言有哪些优点 1.自动做类型推断 2.可以保证类型使用安全 Swif类型说明符 --Swift增加了Tuple表示元组类型 --Swift增加了Optional表示可选类型 常量一 ...