一、源代码的组织

我们通常把公用的自定义函数和类从主程序中分离出来,函数和类的声明在头文件中,定义在程序文件中,主程序中要包含头文件,编译时要和程序文件一起编译。

示例(public.h)

/*
 * 程序名:public.h,公共功能函数声明的头文件,用于测试静态和动态链接
 * 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#ifndef PUBLIC_H
#define PUBLIC_H 1
 
#include <stdio.h>
 
void func();   // 自定义函数的声明
 
#endif

示例(public.cpp)

/*
 * 程序名:public.cpp,公共功能函数定义的程序文件,用于测试静态和动态链接
 * 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include "public.h"  // 包含自定义函数声明的头文件
 
void func()   // 自定义函数的实现
{
  printf("我心匪石,不可转也。我心匪席,不可卷也。威仪棣棣,不可选也。\n");
  //printf("生活美好如鲜花,不懂享受是傻瓜;\n");
  //printf("傻呀傻呀傻呀傻,比不上小鸟和乌鸦。\n");
  //printf("芳草地啊美如画,谁要不去是傻瓜;\n");
  //printf("我是一只傻傻鸟,独在枯枝丫上趴。\n");
}

示例(book265.cpp)

/*
 * 程序名:book265.cpp,此程序用于测试静态和动态链接
 * 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include "public.h"  // 把public.h头文件包含进来
 
int main()
{
  func();
}

编译指令

g++ -o book265 book265.cpp public.cpp

运行效果

image.png

公用函数库的程序文件public.cpp程序文件是源代码,对任何程序员是可见的,没有安全性可言,但是,在实际开发中,出于技术保密或其它方面考虑,开发者并不希望提供公用函数库的源代码。

C/C++提供了一个可以保证代码安全性的方法,把公共的程序文件编译成库文件,库文件时一种可执行代码的二进制形式,可以与其它的源程序一起编译,也可以被操作系统载入内存执行。

库文件分为静态库与动态库。

二、静态库

静态库在编译的时候,主程序文件与静态库一起编译,把主程序与主程序中用到的库函数一起整合进了目标文件。这样做优点是在编译后的可执行程序可以独立运行,因为所使用的函数都已经被编译进去了。缺点是,如果所使用的静态库发生更新改变,我们的程序必须重新编译。

静态库文件名的命名方式是“libxxx.a”,库名前加”lib”,后缀用”.a”,“xxx”为静态库名。

把程序文件public.cpp编译成静态库的指令:

g++ -c -o libpublic.a public.cpp

使用静态库的方法一,直接把调用者源代码和静态库文件名一起编译。

g++ -o book265 book265.cpp libpublic.a

使用静态库的方法二,采用L参数指定静态库文件的目录,-l参数指定静态库名。

g++ -o book265 book265.cpp -L/home/wucz/demo -lpublic

执行程序。

./book265

运行效果。

image.png

注意:1)如果要指定多个静态库文件的目录,用法是“-L/目录1 -L目录2 -L目录3”;2)链接库的文件名是libpublic.a,但链接库名是”public”,不是“libpublic.a”;3)如果要指定多个静态库,用法是“-l库名1 -l库名2 -l库名3”。

三、动态库 

动态库在编译时并不会被连接到目标代码中,而是在程序运行时才被载入,因此在程序运行时还需要指定动态库的目录。

动态库的命名方式与静态库类似,前缀相同,为“lib”,后缀变为“.so” “xxx”为动态库名。

把程序文件public.cpp编译成动态库的指令:

g++ -fPIC -shared -o libpublic.so public.cpp

使用动态库的方法与使用静态库的方法相同。

如果在动态库文件和静态库文件同时存在,优先使用动态库编译。

g++ -o book265 book265.cpp -L/home/wucz/demo -lpublic

执行程序。

./book265

执行程序book265时,出现以下提示。

image.png

这是因为采用了动态链接库的可执行程序在运行时需要指定动态库文件的目录,Linux系统中采用LD_LIBRARY_PATH环境变量指定动态库文件的目录。

采用以下命令设置LD_LIBRARY_PATH环境变量。

export LD_LIBRARY_PATH=/home/wucz/demo:.

注意:1)如果要指定多个动态库文件的目录,用法是“export LD_LIBRARY_PATH=目录1:目录2:目录3:.”,目录之间用半角的冒号分隔,最后的圆点指当前目录。

接下来修改动态库中func函数的代码。

  printf("我心匪石,不可转也。我心匪席,不可卷也。威仪棣棣,不可选也。\n");

改为

  printf("生活美好如鲜花,不懂享受是傻瓜;\n");
  printf("傻呀傻呀傻呀傻,比不上小鸟和乌鸦。\n");
  printf("芳草地啊美如画,谁要不去是傻瓜;\n");
  printf("我是一只傻傻鸟,独在枯枝丫上趴。\n");

重新编译动态库。

g++ -fPIC -shared -o libpublic.so public.cpp

无需重新编译book265,直接执行程序。

./book265

执行效果。

image.png

动态库在编译的时候只做语法检查,并没有被编译进目标代码,当程序执行到动态库中的函数时才调用该函数库里的代码。动态函数库并没有整合进程序,所以程序的运行环境必须提供动态库路径。优点是,如果所使用的动态库发生更新改变,程序不需要重新编译,所以动态库升级比较方便。

四、静态库的优缺点

1、优点

静态链接相当于复制一份库文件到可执行程序中,不需要像动态库那样有动态加载和识别函数地址的开销,也就是说采用静态链接编译的可执行程序运行更快。

2、缺点

1)静态链接生成的可执行程序比动态链接生成的大很多,运行时占用的内存也更多。

2)库文件的更新不会反映到可执行程序中,可执行程序需要重新编译。

五、动态库的优缺点

1、优点

1)相对于静态库,动态库在时候更新(修复bug,增加新的功能)不需要重新编译。

2)全部的可执行程序共享动态库的代码,运行时占用的内存空间更少。

2、缺点

1)使可执行程序在不同平台上移植变得更复杂,因为它需要为每每个不同的平台提供相应平台的共享库。

2)增加可执行程序运行时的时间和空间开销,因为应用程序需要在运行过程中查找依赖的库函数,并加载到内存中。

六、编译的优先级

静态库与动态库各有优缺点,该怎么选择,要看应用的场景。

所谓有得必有失,动态库在程序运行时被链接,故程序的运行速度和链接静态库的版本相比必然会打折扣。然而瑕不掩瑜,动态库的不足相对于它带来的好处在现今硬件下简直是微不足道的,所以链接程序在链接时一般是优先链接动态库的,除非用-static参数指定链接静态库。

七、版权声明

C语言技术网原创文章,转载请说明文章的来源、作者和原文的链接。

来源:C语言技术网(www.freecplus.net

作者:码农有道

如果文章有错别字,或者内容有错误,或其他的建议和意见,请您联系我们指正,非常感谢!!!

 


C语言技术网(www.freecplus.net),粤ICP备19156379号

友情链接: 猿说编程    程序分享   

点击关闭