一次性弄懂gcc编译、链接、动态链接、静态链接

在自己实践的情况下,基本验证了编译,链接(动态链接、静态链接)的过程。

这里简单用自己的语言来说一下编译和链接。

编译:将源文件编译成二进制的目标文件(这里说的编译是大的概念 包括:预处理、编译非汇编、汇编三个步骤)

小步骤(自己的理解):

1、预处理:宏定义替换之类的工作

2、编译非汇编:将源代码经过词法分析、语法分析、语义分析转为汇编代码的过程

3、汇编:将汇编代码转为具体二进制机器码的过程(此时由于还没有进行链接,所以虽然是二进制代码也不可直接执行)

链接:将目标文件中链接到的其他目标二进制文件进行整合成最终可执行二进制文件

这里说整合是因为有两种链接方式

动态链接:最终形成的可执行二进制文件只包含需要链接的目标二进制文件的位置,本身自己的二进制文件并不包含目标二进制文件,最终形成的二进制文件较小,利于目标二进制库进行更新,不用二次编译(只要位置和接口都没变),但是自己单独不可运行。

静态链接:将需要的目标二进制文件打包进自己的二进制文件中,其实就是位置替换,将出现目标二进制文件的位置用目标二进制文件进行位置替换。可独立执行,生成的二进制文件较大,每次目标二进制库更新后,都需要重新进行编译(如果要使用新功能的话)。

这里用个小例子来主要说明动态编译和静态编译过程:

首先目录结构很简单:

只有四个文件:main.cc,test.h,add.cc,sub.cc 都在同一目录下,代码也很简单:

main.cc

#include 
#include "test.h"

using namespace std;

int main(){
    int m,n;
    cout<<"input two numbers"<>m>>n;
    cout<#ifndef __TEST_H_
#define __TEST_H_

int add(int a,int b);
int sub(int a,int b);

#endif

add.cc

#include "test.h"
int add(int a,int b)
{
    return a + b;
}

sub.cc

#include "test.h"
int sub(int a,int b)
{
    return a - b;
}

首先采用直接编译和链接 一步完成的方式。然后采用动态链接的方式生成目标可执行二进制:

首先生成动态链接库,然后执行,注意源文件跟-o之间多空一格空格,不然容易不识别。

注意:生成动态链接库时,如果头文件跟源文件不在同一路径下,需要指定-I头文件路径

一次性弄懂gcc编译、链接、动态链接、静态链接_第1张图片

g++ -fpic -shared add.cc sub.cc   -o libmymath.so

g++ main.cc libmymath.so -Wl,-rpath=$HOME/cpp-code/demo -o main.out

结果如下:

一次性弄懂gcc编译、链接、动态链接、静态链接_第2张图片

由于头文件就在当前目录下,所以不用指定头文件路径,这里最后运行时只声明了需要链接的库为libmymath.so 和 动态链接库 搜索路径用-Wl,-rpath来指定。

下面采用分开编译和链接 两个步骤,仍然使用动态链接生成目标可执行二进制:

注:这里需要注意引入libmymath.so的时候,如果libmymath.so跟源文件不在同一路径下,需要将指定相对路径或绝对路径,不然会报找不到libmymath.so文件。参考如下两个图

一次性弄懂gcc编译、链接、动态链接、静态链接_第3张图片

 一次性弄懂gcc编译、链接、动态链接、静态链接_第4张图片

#编译汇编为二进制文件
g++ -c main.cc -o main.o

g++ main.o libmymath.so -Wl,-rpath=$HOME/cpp-code/demo -o main2.out

一次性弄懂gcc编译、链接、动态链接、静态链接_第5张图片

 这里可以看到成功可以独立成功编译,说明在编译和汇编阶段,只需要能够找得到头文件即可,对于具体库的实现可以放在链接阶段再去做。

这里注意 gcc里面-L 是去指定静态链接库的路径,不能用来指定动态链接库。

不能找到动态链接库。

下面实践静态链接库

首先生成静态链接库:

g++ -c add.cc sub.cc

ar rcs libmymath.a add.o sub.o

 

然后执行二进制,链接静态链接库,这里我把静态链接库放在static目录下了

g++ -static main.cc -L=$HOME/cpp-code/demo/static -lmymath -o main-static.out

命令中 -static 声明采用静态链接库, -L指定静态链接库查找位置, -l(小写的L)指定要链接的库名

默认gcc 链接采用的是动态链接,动态链接失败后去找静态链接。

在这里大家可以看下两个可执行文件的文件大小。红色的为静态链接可执行文件,蓝色为动态链接可执行文件,可以看到动态链接文件大小比静态链接文件大小小很多。

一次性弄懂gcc编译、链接、动态链接、静态链接_第6张图片

可以看到这里没有指定链接类型,但是动态链接的路径没有指定,就采用静态链接。

一次性弄懂gcc编译、链接、动态链接、静态链接_第7张图片

最后ldd里面也没有看到动态链接到mymath.so的库,说明是静态链接。

二者都指定后,采用的是动态链接:

g++ main.cc libmymath.so  -Wl,-rpath=$HOME/cpp-code/demo -I/home/wzy/cpp-code/demo/include -L=$HOME/cpp-code/demo/static -lmymath -o main-default-dynamic3.out

一次性弄懂gcc编译、链接、动态链接、静态链接_第8张图片

其中生成的可执行文件中,含有libmymath.so的动态链接库的位置。

测试将动态链接库跟头文件分离,然后编译动态链接,这里仍然可以生成可执行二进制文件。说明只要编译时让其找到头文件位置即可。

g++ main.cc libmymath.so  -Wl,-rpath=$HOME/cpp-code/demo -I/home/wzy/cpp-code/demo/include  -o main-default-dynamic2.out

可以看到这里并没有test.h头文件

一次性弄懂gcc编译、链接、动态链接、静态链接_第9张图片

可以尝试将代码进行修改,然后测试动态链接库

add.cc 这里代码不严谨,不要关注这些细节。

#include "test.h"
#include 
int add(int a,int b)
{
    std::cout<<"version 2"<

sub.cc 

#include "test.h"
#include 
int sub(int a,int b)
{
    std::cout<<"version 2"<

这里还是用刚才的main-default-dynamic3.out 执行,注意生成的动态链接库一定要名称跟以前一样,然后放入之前编译时指定参数的位置。

这里就可以直接运行,而不用再次编译:

一次性弄懂gcc编译、链接、动态链接、静态链接_第10张图片

 最后输出使用的是更新后的动态链接库 version2

这里记录一下,也加深一下自己的理解。希望对刚入门的小伙伴有帮助,大佬勿喷。

后面顺便总结一下常用的一些链接查看小技巧。

要查找正在使用哪个库,可以运行

/sbin/ldconfig -p | grep stdc++

libstdc ++ 3.4.0及更高版本的兼容版本列表由提供,这里使用了strings。strings 命令是二进制工具集GNU Binutils 的一员,用于打印文件中可打印字符串,strings命令在对象文件或二进制文件中查找可打印的字符串

strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep LIBCXX

使用效果大致如下:

一次性弄懂gcc编译、链接、动态链接、静态链接_第11张图片

 

你可能感兴趣的:(c++,c++,ubuntu,gnu,linux)