• 动态链接库和静态链接库介绍和实例(二)


    静态链接库创建和使用实例

    环境:win10

               VS2015

    静态链接库的创建和使用都比较的简单,我们直接看例子理解就可以了。

    一、

    在vs2015中新建一个Win32控制台的静态链接库空工程static_lib,在工程中创建文件static_lib.h和static_lib.cpp这两个文件,static_lib.h和static_lib.cpp的源代码如下:

    //.h
    #pragma once
    
    #ifndef LIB_H
    #define LIB_H
    
    
    extern "C" int add(int a, int b); //声明为C调用方式的外部调用函数
    
    
    #endif
    //.cpp
    #include "static_lib.h"
    
    int add(int a, int b)
    {
    	return a + b;
    }

    直接编译这个工程,在debug目标中就会自动生成static_lib.lib。

    在static_lib工程的工作区中新建一个空的libcall工程,它只添加一个文件test.cpp,代码如下:

    //test.cpp
    #include "../static_lib/static_lib.h"
    #include <stdio.h>
    #include <cstdlib>
    #pragma comment(lib, "..\debug\static_lib.lib")
    
    int main(int argc, char *argv[])
    {
    	printf("4 + 5 = %d
    ", add(4, 5));
    	system("pause");
    }

    编译运行结果如下:

    静态链接库的调用就是这么简单,或许我们每天都在用,可是我们没有明白这个概念。代码中#pragma comment(lib, "..\debug\static_lib.lib")的意思是指本文件生成的.obj文件应与tatic_lib.lib一起链接,此处用的相对地址指明static_lib.lib的位置。如果不用#pragma comment指定,则可以直接在vs中设置,如下图,在libcall工程的属性页面,通过链接器->常规->附加库目录添加tatic_lib.lib所在的目录,通过链接器->输入->附加依赖项添加static_lib.lib到项目中。

    这个静态链接库的例子至少让我们明白了库函数是怎么回事,它们是哪来的。我们现在有下列模糊认识了:

      (1)库不是个怪物,编写库的程序和编写一般的程序区别不大,只是库不能单独执行;

      (2)库提供一些可以给别的程序调用的东东,别的程序要调用它必须以某种方式指明它要调用之。

      以上从静态链接库分析而得到的对库的懵懂概念可以直接引申到动态链接库中,动态链接库与静态链接库在编写和调用上的不同体现在库的外部接口定义及调用方式略有差异。

    二、

    生成和使用静态库

    生成静态库

    新建项目--win32项目--填写项目名--确定--下一步--应用程序类型:选择静态库

    静态库项目没有main函数,也没有像dll项目中的dllmain。

    创建项目后添加.h文件,添加相应的导出函数、变量或类,如下所示

    //.h
    #pragma once
    #ifndef _MYLIB_H_
    #define _MYLIB_H_
    
    void fun(int a);
    
    extern int k;
    
    class testclass
    {
    public:
    	testclass();
    	void print();
    };
    
    #endif
    //.cpp
    #include "stdafx.h"
    #include "mylib.h"
    #include <iostream>
    
    void fun(int a)
    {
    	std::cout << a << "lib gen
    ";
    }
    
    int k = 222;
    
    testclass::testclass()
    {
    	std::cout << "123
    ";
    }
    
    void testclass::print()
    {
    	std::cout << "this is testcalss
    ";
    }

    编译工程后就会生成一个.lib文件

    使用静态库

    需要.h文件,.lib文件

    (1)设置项目属性--C/C++目录--General--设置附加包含目录--.h所在的路径(这步之后源文件就可以寻找到头文件了)

    (1)设置项目属性--Linker--General--设置附加库目录--.lib所在的路径

    (2)将lib添加到项目属性--Linker--Input--附加依赖项--.lib文件名称(或者直接在源代码中加入#pragma comment(lib, “**.lib”))

    (3)在源文件中添加.h头文件

    然后就像平常一样调用普通函数、类、变量,举例如下:

    #include <iostream>
    #include "mylib.h"
    //#pragma comment(lib, "static_lib.lib")
    using namespace std;
    int main()
    {
    	fun(4);
    	std::cout << k << std::endl;
    	testclass tc;
    	tc.print();
    	int response;
    	cin >> response;
    }

    三、

    1.VS2015生成lib文件
    在VS2015中新建项目->Win32项目->勾选静态链接库,然后添加所需要的类,包括.h文件和.cpp文件。下面的例子中,我添加了两个类,分别是twomain类和student类,相应的代码如下:

    //twomain.h:
    #pragma once
    #include"stdafx.h"
    #include"student.h"
    #include<iostream>
    #include<string>
    using namespace std;
    
    class twomain
    {
    public:
    	twomain();
    	~twomain();
    	void setStn(string name, int age);
    	student getStn();
    	void setCity(string city);
    	string getCity();
    
    	void toString();
    private:
    	student stn;
    	string city;
    };
    //twomain.cpp
    #include "stdafx.h"
    #include "twomain.h"
    
    twomain::twomain()
    {
    }
    twomain::~twomain()
    {
    }
    void twomain::setCity(string city) {
    	this->city = city;
    }
    string twomain::getCity() {
    	return this->city;
    }
    void twomain::setStn(string name, int age) {
    	this->stn.setName(name);
    	this->stn.setAge(age);
    }
    student twomain::getStn() {
    	return this->stn;
    }
    void twomain::toString() {
    	cout << "name is " << this->stn.getName() << ",age is " << this->stn.getAge() << ",city is " << this->city << endl;
    }
    //student.h
    #pragma once
    #include<iostream>
    #include<string>
    using namespace std;
    class student
    {
    public:
    	student();
    	~student();
    	void setName(string name);
    	string getName();
    	void setAge(int age);
    	int getAge();
    	void toString();
    
    private:
    	string name;
    	int age;
    };
    //student.cpp
    #include "stdafx.h"
    #include "student.h"
    student::student()
    {
    }
    student::~student()
    {
    }
    void student::setName(string name) {
    	this->name = name;
    }
    string student::getName() {
    	return this->name;
    }
    void student::setAge(int age) {
    	this->age = age;
    }
    int student::getAge() {
    	return this->age;
    }
    void student::toString() {
    	cout << "name is " << this->name << ",age is " << this->age << endl;
    }

    然后点击工具栏菜单中的生成->生成解决方案,就可以在项目文件目录的debug目录下看到生成的lib文件。主要要考虑清楚自己的lib应用的场景,是debug模式还是release模式,是x86还是x64平台使用,这些要严格按照需要选择,不然后面使用时很容易出一些错误。

    2.使用lib文件
    生成好了lib文件之后,我们新建一个空项目,来测试生成的lib是否好用。在新建的空项目中添加main.cpp文件,并在该文件中添加如下代码:

    #include"twomain.h"
    //#pragma comment(lib, "../Debug/static_lib.lib")
    int main() {
    	twomain t;
    	t.setCity("江西");
    	t.setStn("刘伟", 23);
    	t.toString();
    	system("pause");
    
    	return 0;
    }

    这时还没有引入.h文件和.lib文件,所以会有错误,引入.h文件和.lib文件的操作如下:
    1.引入.h文件。引入.h文件,使得在#include .h文件时不会报错,也就引入了类的声明,类的具体实现在.lib文件中。右键项目->属性->配置属性->C/C++->常规->附加包含目录,在附加包含目录中添加包含该lib中使用到所有的.h的文件夹的路径。项目首先会在自己项目中搜索是否存在.h文件,如果发现没有就会去附加包含目录中搜索.h文件。如下图所示: 

    2.引入.lib文件。.h文件仅仅包含了类或方法的定义,没有具体的实现,具体的实现在.lib文件中。右键项目->属性->配置属性->链接器->常规->附加库目录,在附加库目录中添加包含该lib文件的文件夹路径。然后再在链接器->输入->附加依赖项中,加入该lib的名字,包括文件格式.lib。(或者在源代码中加入#pragma comment(lib, "../Debug/static_lib.lib")),如下图所示: 


    然后程序就可以运行了,参考main.cpp代码,运行结果截图如下:

    四、二分搜索生成库

    1、新建一个项目,选择Win32项目

    2、选择静态库,预编译头可以加也可以不加,这里我不加

    如果没有选静态库,可以在项目/工程属性/配置属性/常规/项目默认值配置类型中修改

    3、添加.cpp文件和.h文件

    (1)demo.cpp文件

    #include<iostream>
    #include<vector>
    #include"BinarySearchLib.h"
    using namespace std;
     
    //递归方法
    int Binary_Search_Recursion(vector<int> v, int begin, int end, int key)
    {
    	if (begin > end)
    		return -1;
    	int mid = (begin + end) >> 1;
    	if (v[mid] > key)
    		return Binary_Search_Recursion(v, begin, mid - 1, key);
    	else if (v[mid] < key)
    		return Binary_Search_Recursion(v, mid + 1, end, key);
    	else
    		return mid;
    }
     
    //非递归方法
    int Binary_Search(vector<int> v, int begin, int end, int key)
    {
    	if (begin > end)
    		return -1;
    	int mid = 0;
    	while (begin <= end)
    	{
    		mid = (begin + end) >> 1;
    		if (v[mid] > key)
    			end = mid - 1;
    		else if (v[mid] < key)
    			begin = mid + 1;
    		else
    			return mid;
    	}
    	return -1;
    }

    (2)BinarySearchLib.h文件

    #ifndef  _BinarySearchLib_H
    #define  _BinarySearchLib_H
     
    #include<vector>
    using namespace std;
     
    int Binary_Search_Recursion(vector<int> v, int begin, int end, int key);
    int Binary_Search(vector<int> v, int begin, int end, int key);
     
    #endif
     

    4、生成解决方案


     

    这时候可以看到下方显示成功

    5、把解决方案所在文件夹的Debug文件夹下找到.lib文件

    在这里我新建了一个控制台程序项目

    (2)把.h文件添加到工程中,并在工程中添加库#pragma comment(lib,"BinarySearchLib.lib")

    如果不写#pragma comment(lib,"BinarySearchLib.lib")可以在项目/工程属性/链接器/输入/附加依赖项中添加BinarySearchLib.lib。 

    #include <iostream>
    #include <vector>
    #include "BinarySearchLib.h"
    
    using namespace std;
    
    #pragma comment(lib,"BinarySearchLib.lib")
    
    int main()
    {
    
    	vector<int> data = { 1,2,3,4,5,6,7,8 };
    	int res = Binary_Search(data, 0, data.size() - 1, 4);
    	cout << res << endl;
    	cin >> res;
    	return 0;
    
    }

    运行结果:

    理论补充:

    1、.h .lib .dll文件关系

    摘自:http://www.cnblogs.com/zcshan/archive/2010/12/03/1895605.html

    .h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。

    附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件。如果要完成源代码的编译和链接,有头文件和lib就够了。如果也使动态连接的程序运行起来,有dll就够了。在开发和调试阶段,当然最好都有。

    .h .lib .dll三者的关系是:

    H文件作用是:声明函数接口

    DLL文件作用是: 函数可执行代码

    当我们在自己的程序中引用了一个H文件里的函数,编链器怎么知道该调用哪个DLL文件呢?这就是LIB文件的作用: 告诉链接器 调用的函数在哪个DLL中,函数执行代码在DLL中的什么位置,这也就是为什么需要附加依赖项 .LIB文件,它起到桥梁的作用。如果生成静态库文件,则没有DLL ,只有lib,这时函数可执行代码部分也在lib文件中

    目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。静态库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。动态库一般会有对应的导入库,方便程序静态载入动态链接库,否则你可能就需要自己LoadLibary调入DLL文件,然后再手工GetProcAddress获得对应函数了。有了导入库,你只需要链接导入库后按照头文件函数接口的声明调用函数就可以了。导入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。

    一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。在动态库的情况下,有两个文件,而一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。
     

    参考:https://blog.csdn.net/u011391629/article/details/53636436

               https://blog.csdn.net/jeryjeryjery/article/details/70893616#commentBox

               http://www.cnblogs.com/TenosDoIt/p/3203137.html

  • 相关阅读:
    BZOJ 1726 洛谷 2865 [USACO06NOV]路障Roadblocks【次短路】
    BZOJ 1699 [Usaco2007 Jan]Balanced Lineup排队
    洛谷 2777 [AHOI2016初中组]自行车比赛
    BZOJ 1832、1787 洛谷 4281 [AHOI2008]紧急集合
    BZOJ 4385 洛谷3594 POI2015 WIL-Wilcze doły
    【HDU 3037】Saving Beans(卢卡斯模板)
    【POJ 1061】青蛙的约会(EXGCD)
    【HIHOCODER 1469 】福字(DP)
    【BZOJ 1076】[SCOI2008]奖励关(期望)
    【HIHOCODER 1509 】 异或排序
  • 原文地址:https://www.cnblogs.com/oneDongHua/p/14264068.html
Copyright © 2020-2023  润新知