静态编译需要静态库(libxxx.a),静态编译成可执行程序后(一般程序会比较大),可单独运该程序,不需要依赖其他文件。
动态编译需要动态库(libxxx.so),动态编译成可执行文件,需要与所需要的动态库一起存在,才可运行。
静态库与动态库的制作可直接使用gun编译器
在Windows下很好理解这些概念,因为当你需要引入一个动态库(dll)或者一个静态库(lib)时一般的步骤是:
因此,就很好理解.h文件、.dll文件和.lib文件的关系:
生成静态库。
//StaticMath.h
#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);
};
//StaticMath.cpp
#include "StaticMath.h"
double StaticMath::add(double a, double b)
{
return a + b;
}
//testStatic.cpp
#include
#include "StaticMath.h"
int main()
{
double a = 1.2;
double b = 2.4;
std::cout << "a+b="<<StaticMath::add(a, b)<<std::endl;
system("pause");
return 0;
}
配置头文件引用目录:项目属性->C/C+±>常规->附加包含目录->静态库头文件所在的目录。
配置静态库的目录:项目属性->链接器->命令行->其它选项->添加静态库的绝对路径。
Windows下使用动态库就要比静态库复杂多了。
创建动态库
//DynamicMath.h
// 下列 ifdef 块是创建使从 DLL 导出更简单的宏的标准方法。
// 此 DLL 中的所有文件都是用命令行上定义的 DYNAMICMATH_EXPORTS
// 符号编译的。在使用此 DLL 的任何其他项目上不应定义此符号。
// 这样,源文件中包含此文件的任何其他项目都会将 DYNAMICMATH_API
// 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的符号视为是被导出的。
#ifdef DYNAMICMATH_EXPORTS
#define DYNAMICMATH_API __declspec(dllexport)
#else
#define DYNAMICMATH_API __declspec(dllimport)
#endif
// 此类是从 DynamicMath.dll 导出的
class DYNAMICMATH_API CDynamicMath {
public:
CDynamicMath(void);
// TODO: 在此添加您的方法。
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);
};
//示例导出变量声明
extern DYNAMICMATH_API int nDynamicMath;
//示例导出函数声明
DYNAMICMATH_API int fnDynamicMath(void);
这里编译器生成的模板中,CDynamicMath类已经被DYNAMICMATH_API(从DynamicMath.dll导出)修饰,因此类中的成员函数无需再用DYNAMICMATH_API修饰。
// DynamicMath.cpp : 定义 DLL 应用程序的导出函数。
#include "stdafx.h"
#include "DynamicMath.h"
// 这是导出变量的一个示例
DYNAMICMATH_API int nDynamicMath=0;
// 这是导出函数的一个示例。
DYNAMICMATH_API int fnDynamicMath(void)
{
return 42;
}
// 这是已导出类的构造函数。
// 有关类定义的信息,请参阅 DynamicMath.h
CDynamicMath::CDynamicMath()
{
return;
}
//我自己添加的导出函数
double CDynamicMath::add(double a, double b)
{
return a + b;
}
DynamicMath.dll(动态链接库)
DynamicMath.lib(动态链接库的导入库)
有了.h文件 、动态链接库、导入库这三件套,我们就可以很容易的静态加载(即隐式链接的方式)动态链接库了。否则的话,就需要自己LoadLibrary调入DLL文件,再手动GetProcAddress获得对应函数了(又称显式链接)。
有了导入库,你只需要链接导入库后按照头文件函数接口的声明调用函数就可以了。导入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。
一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。在动态库的情况下,有两个文件,而一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。
这里使用隐式链接(静态加载)的方式。
#include "DynamicMath.h"
#include
int main()
{
int num = fnDynamicMath();//自动生成的示例导出函数
std::cout << num << std::endl;
double a=1.2;
double b=2.5 ;
std::cout << CDynamicMath::add(a, b) << std::endl;
system("pause");
return 0;
}
配置头文件目录:项目属性->C/C+±>常规->附加包含目录。
配置导入库(附加依赖项)目录:项目属性->链接器->常规->附加库目录
除此之外还需要配置具体附加依赖项.lib:配置属性->链接器->输入-> 在“附加依赖项”里添加“DynamicMath.lib”。
配置动态链接库目录:项目属性->配置属性->VC++目录->库目录。
编译运行测试动态库项目,发现错误:说系统找不到DynamicMath.dll文件,事实上,我们需要将dll文件拷贝到测试工程的Debug目录下后正常运行: