编写及运行c++程序的整个过程的各种问题详解

源文件main.cpp中包含的 头文件 及 头文件的实现—库文件 的区别及联系:

1–头文件:
头文件中包含函数的声明和(部分)定义。通常被包含在其他源文件中,是其他程序调用该头文件中定义的函数的接口。
头文件以 .h 结尾,因为其内容是ASCII码的,用文本编辑器可以直接打开查看。

2–库文件:
声明放在头文件中,而库文件中包含函数的定义。库文件是已经编译好的、可以复用的、二进制的代码,可以被操作系统载入内存中执行。用文本编辑器打开查看的时候会显示乱码。
头文件以 .a 或者 .so 结尾(linux系统下),分为静态库动态库。linux系统下以 .a 结尾的库文件为静态库,以.so结尾的为动态库。
.a :静态库。
.so:动态库。

2.1–静态库与动态库的区别是什么?
所谓静态及动态是指链接过程。这里需要温习一下将一个程序源代码编译成可执行程序的步骤:

源文件(.cpp .h等)------>预编译------>编译------->汇编-------->链接 (删除线是指有的程序不需要这一步)------->可执行文件

静态库与动态库区别来自 “链接阶段” 如何处理库。

2.1.1—静态库
之所以称之为静态库,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中(被打包的可执行文件中包含静态库)。因此对应的链接方式称为静态链接。

静态库的特点:
1-静态库对函数库的链接是放在编译时期完成的。
2-程序在运行时与函数库再无瓜葛,移植方便(因为可执行程序中已经打包了一份库函数了)。
3-浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。

linux系统下创建及使用静态库:

1-创建StaticMath.h文件:

StaticMath
 {
       public:
         StaticMath();
         ~StaticMath();

         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;}
double StaticMath::sub(double a, double b){return a-b;}
double StaticMath::mul(double a, double b){return a*b;}
double StaticMath::div(double a, double b){return a/b;}

2-首先将代码文件编译成目标文件.o(StaticMath.o)。

g++ -c StaticMath.cpp //注意g++的参数为-c,不然直接编译成可执行文件

3-然后,利用 ar 工具将目标文件打包成 .a 静态库文件。

ar -crv libstaticmath.a StaticMath.o
//注意这里的
//Linux静态库命名规范,必须是"lib[your_library_name].a":lib为前缀,中间是静态库名,扩展名为.a

这样就生成了静态库 libstaticmath.a 。
比较大的项目会编写makefile文件(或者其包装的上层CMakeLists文件)来生成静态库,像咱们上面这样通过命令生成静态库真是太麻烦了。

如何编写 CMakeLists.txt 文件以生成静态库文件:

cmake_minimum_required(VERSION 3.5.1)                                             
project(static_lib_test)
set(CMAKE_CXX_STANDARD 14)
//生成库文件需要加这行代码,其中staticmath为欲要生成的库文件名,这样生成的库文件名会自动加lib
//STATIC 说明由StaticMath.cpp生成的库文件为静态库文件,如果向生成动态库文件,替换为SHARED
add_library(staticmath STATIC StaticMath.cpp) 

如何使用静态库:

UseLib.cpp 如下:

#include <iostream>                                
#include "StaticMath.h" //1--包含库的头文件                            
using namespace std;                               
            
int main(int argc, char** argv)                    
{            
      double a=10,b=2;                           
      cout << "a+b="<<StaticMath::add(a,b)<<endl;
     //...                                                                     
}

CMakeLists.txt 如下:

cmake_minimum_required(VERSION 3.5.1)                                             
project(static_lib_test)
set(CMAKE_CXX_STANDARD 14)
add_library(staticmath STATIC StaticMath.cpp)
add_executable(UseLib UseLib.cpp)
target_link_libraries(UseLib staticmath) //2--链接到staticmath库

2.1.2—动态库

根据上面对静态库的了解,我们知道,每生成一份可执行文件(特指用到库文件函数的),如果都需要链接静态库,那么可执行文件的二进制文件中都会被复制一份静态库代码。这就造成了空间的浪费。
在这里插入图片描述
另外一个问题是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库更新了,那么使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新,想象一个LOL这么大型的游戏,如果像这样更新一遍的话,用户可能会被烦死)。

动态库在程序编译时并不会被连接到目标代码中,而是只有在程序运行时才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
在这里插入图片描述动态库的特点:

1-动态库把对一些库函数的链接载入推迟到程序运行的时期。
2-可以实现进程之间的资源共享。(因此动态库也称为共享库
3-将一些程序升级变得简单。
4-甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。

Window与Linux执行文件格式不同,在创建动态库的时候有一些差异。

1-在Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数做出初始化的入口,通常在导出函数的声明时需要有_declspec(dllexport)关键字。
2-Linux下gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。
与创建静态库不同的是,不需要打包工具(ar、lib.exe。当然如果是在CMakeLists.txt文件中编写,会自动调用这些工具,不需要我们去显式的打包),直接使用编译器即可创建动态库。

linux系统下创建及使用动态库:

参考:https://blog.csdn.net/dd_hello/article/details/81782934?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160733757919724827621160%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=160733757919724827621160&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-1-81782934.pc_search_result_no_baidu_js&utm_term=%E9%9D%99%E6%80%81%E5%BA%93%E4%B8%8E%E5%8A%A8%E6%80%81%E5%BA%93%E5%8C%BA%E5%88%AB&spm=1018.2118.3001.4449

动态链接库的名字形式为 libxxx.so,前缀是lib,后缀名为“.so”。

通过编写 CMakeLists.txt 来生成动态库我们已经在上面通过演示静态库来类比了。我们再重复一遍:
还是原来的头文件与源文件。不写了。

编写 CMakeLists.txt 文件:

cmake_minimum_required(VERSION 3.5.1)                                                                                   
project(static_lib_test)
set(CMAKE_CXX_STANDARD 14)
add_library(staticmath SHARED StaticMath.cpp)
add_executable(UseLib UseLib.cpp)
target_link_libraries(UseLib staticmath)

通过上面的讲解,我们知道,当可执行文件依赖静态库的时候,生成的可执行文件中复制了一份静态库文件,因此,运行的时候,不会担心静态库的位置。但是对于依赖动态库的可执行文件来说,只有在运行的时候才会去找动态库,这就需要找的时候知道动态库在哪里。

如何让系统能够定位到这个动态库:
1)当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。此时就需要系统动态载入器(dynamic linker/loader)。

2)对于elf格式的可执行程序,是由 ld-linux.so* 来完成的,它先后搜索 elf 文件的 DT_RPATH 段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache 文件列表 —/lib/,/usr/lib 目录找到库文件后将其载入内存。

如何让系统能够找到它

1-如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。

2-如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下:

2-1:编辑/etc/ld.so.conf文件,加入库文件所在目录的路径

2-2:运行ldconfig ,该命令会重建/etc/ld.so.cache文件

ldd UseLib :查看可执行文件 UseLib 依赖的动态库。
没有查看所依赖的静态库的命令。可能是因为可执行文件不关系(因为毕竟编译好的文件中就复制了一份静态库在里面了)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值