头文件.h、动态链接库.dll、静态链接库.lib的区别与应用总结
一、头文件.h
1、函数声明与函数定义
函数声明:只是对编译系统的一个说明,是对已定义的函数的返回值类型等信息的说明,函数声明相当于只强调了函数原型,在执行声明时不为其开辟专门的内存空间。
函数定义:函数的定义是一个完整的函数单元,包含函数类型、函数名、形参及形参类型、函数体等,定义函数时相当于给出了函数实体,在执行定义时会开辟专门的内存空间。
由于编译器的编译顺序是命令行从上至下,因此调用一个函数时,在该调用命令之前应该要包含此函数的定义,否则编译器就会找不到函数实体导致错误。然而对于函数定义在后,但在定义前就要使用的情况,我们就需要先给出一个声明,告诉编译器这个“未定义”函数的原型,具体的执行过程(即定义)在其他的位置。在工程中,有的函数定义并不在本项目中,而可能在别的文件里,这种情况下也要进行声明,让编译器去其他位置寻找函数实体。
2、头文件
设想在一个项目中要进行多次声明,使整个项目“无关信息”占用空间,影响整个代码的简洁性和可读性,因此引入头文件。
所谓头文件,就是专门存放函数声明的接口说明文件。例如代码1所示的代码片就为一个典型的头文件函数声明,其声明了有一个返回值为int类型且只有一个int类型参数的名为x_x的函数,而具体的实现过程在相应的源文件中(如代码2)
#prama once #ifndef STATICLIB_H #define STATICLIB_H extern "C" int x_x(int); #endif
代码1 头文件接口说明
#include "staticLib.h" int x_x(int x){ return x*x; }
代码2 源文件函数定义
引入头文件的好处是,其只用了一行形如#include “xx.h”(若是链接库还要再加上一行链接命令)的命令,xx.h里包含了本项目所有需要的函数声明,那么接下来的源文件直接调用函数即可,而不需要再关注因为没有声明函数而造成的错误。
注1
在CPP源文件的函数和全局变量定义中加入extern ”C”表示允许C源文件访问该函数和全局变量,相当于是CPP对C的扩展。
3、模块化编程
如果现在考虑一个工程,其要求使用到很多数学公式和物理函数,那么在主函数项目里对这些数学、物理函数进行定义再调用有诸多弊端:
①大量的函数定义和主函数混杂在一起,使主函数要利用这些数学、物理函数实现的目标可读性变差。
②单就数学和物理这两种主要函数分支,定义相互交叉较为混乱,在对工程进行改良或者维护时极为困难。
……
现在如果将编程模块化,定义一个模块为数学函数,另一个模块为物理函数。这两个类型的模块分别用头文件声明,源文件实现。而后在主工程内,只需要引入这两个模块的头文件,就可以直接调用这些函数,免去的主工程里繁杂的函数定义和声明等等,使主功能突出可读,且维护升级工程函数时只需要修改对应模块的头文件和源文件的一部分即可,而不需要从头阅读整个工程或是对主工程文件进行变动。可以说,头文件的引入是模块化编程的基础
二、链接库
在进行工程开发时,应用程序(.exe)往往需要使用多个模块。按照前述头文件模块化编程的方法,在主工程里要导入相应的头文件与头文件对应的源文件才能进行编译。这样做存在的一个很大的问题是应用程序里包含了所有底层函数的源代码,在应用时开发者往往不希望用户看到这些底层函数的实现方法,因此就引入库函数的概念,将所有函数的底层实现进行封装。
因此库文件模块化编程向用户提交的是头文件(函数声明,告诉用户函数使用方法)与库文件(底层函数实现的封装,用户看到的是二进制文件);头文件模块化编程向用户提交的是头文件和源文件(底层函数的具体实现)
通过对比可以看出,库函数具有以下优点:
①提高应用程序底层函数的安全性和稳定性
②减少重复编译时间,增强程序的模块化
库文件是在程序链接时链接进目标程序中的,因此也称为链接库。就具体实现过程和特点而言分为两种:静态链接库和动态链接库
1、动态库与静态库的优缺点
注2
DLL地狱:当计算机上的DLL版本与创建程序时使用的版本不同时会出现DLL地狱现象。DLL没有用于向后兼容的内置机制,甚至对DLL的微小更改使其内部结构与以前的版本不同,尝试使用它们通常会导致应用程序崩溃。静态库避免了这个问题,因为用于构建应用程序的版本包含在其中,因此即使系统上的其他位置存在较新的版本,这也不会影响应用程序。
2、动态库与静态库的特点
动态库由lib和dll组成,lib相当于dll的导出库,为了区别起见,称lib为导出库,dll为共享库(动态库)。dll动态库记录的是函数的具体实现过程,只不过是封装的二进制文件;导出库记录的是地址符号表等信息,以确保工程在执行时可以根据lib来找到dll中的具体执行环节。
静态库只有lib文件,lib在此时也包含了函数的具体执行环节。
因此无论是静态库还是动态库,对它们的引用都需要以lib文件为桥梁,除了包含库对应的头文件外,还需要命令#pragma comment(lib,“xx.lib”)对库文件进行链接
C标准库如iostream等属于静态库,但调用库函数时并不需要用链接语句,这是因为编译器对这些标准库进行了默认链接,对这类标准库的引用,头文件采用#include <xxx.h>的形式告诉编译器这是标准库。而对于自定义的静态库,编译器不会默认链接,因此头文件采用#include”xxx.h”的形式并且要手动配置链接库。
此外,在动态库的编写时,头文件中声明的函数要使用__declspec(dllexport)语句修饰,这样整个dll文件才会导出库lib,也才能通过链接lib来使用dll