[Linux基础——Lesson6.编译器gcc/g++以及动静态库的认识]
目录
前言
一、✅对程序的认知 && 初识gcc
1、程序是如何诞生的?
2、gcc的初步认识
3、如何使用gcc
二、✅gcc逐步分析程序的翻译环境
🧐1、预编译【进行宏替换】
🗯️预编译阶段的核心工作
🗯️GCC 预编译相关指令 GCC 编译器通过-E选项控制预编译过程
🧐2、编译【C语言——>汇编语言】
🗯️编译阶段的四大核心任务
🗯️编译阶段的输出与 GCC 指令
🧐3、汇编【汇编语言——>可重定位目标二进制文件】
🗯️汇编阶段的核心任务
🗯️汇编阶段的输出与 GCC 指令
🧐4、链接【生成可执行文件或库文件】
🗯️链接阶段的核心任务
🗯️链接阶段的输出与 GCC 指令
链接的两种方式
🗯️总结
🌟完整编译流程总结
5、巧记gcc命令选项与生成文件后缀【⭐】
三、✅gcc与g++的区别
1️⃣基础共性:用法框架一致
2️⃣核心差异:编译场景与细节
3️⃣关键结论:选择逻辑
四、🗯️谈谈链接的过程【动静态库的理解】
1、 库的初步认识
📖拓展【ldd】指令
2、 动态库与静态库
一、基本概念
二、本质区别与核心原理
三、技术特性对比
四、开发实践考量
五、总结
3、动静态库的感性理解【小蚂蚁网吧🐜】
静态库 vs 动态库 —— 再用 “电脑归属” 总结核心区别
做做实验【动静态链接的使用】
总结
结束语:
前言
GCC/G++ 是 GNU 项目开发的主流编译器套件,其中 GCC 主要编译 C 语言代码,G++ 是 GCC 的 C++ 编译器前端,二者是开发 C/C++ 程序的核心工具。
一、✅对程序的认知 && 初识gcc
1、程序是如何诞生的?
对于一个程序来说,从【编辑】——>【编译】——>【链接】——>【运行】总共要经过这些步骤算是一个程序完成的诞生过程。如果我们要去细谈其中的过程,又可以像下面这样分化👇
- 我们需要将这个【源程序】首先转变为【可执行程序】,中间的这一流程就被成为翻译环境,这一环境也就是我们本文所学习的
gcc
所要做的事情。在下一模块我就会对预编译
、编译
、汇编
、链接
这四个小模块展开做讲解📖
2、gcc的初步认识
- 🎯 gcc英文全名为GNU Compiler Collection,早期的gcc编译器主要用于C语言编译,但是经过几十年的发展,gcc编译器可以用于多种语言的编译,例如C++、Go等目前较为主流的语言。
- 🎯 熟悉gcc编译器是对于我们开发C/C++程序的底层基本功,虽然目前各厂商的开发IDE已经非常智能,从某种程度上已经把程序员从底层代码的编译、部署等工作解放出来,但是如果需要开发大型C++项目或者对于编译过程进行优化,那么gcc编译器是需要进行了解和深入的
- 对于gcc而言,它是一款Linux中的编译器,通常和我们之前所讲的Linux 编辑器vim配合使用。
- 我们对【编辑】【编译】应该要有一个很清晰的认识,我们平时在写C/C++代码的时候,大多都是在一些集成开发环境上(IDE)进行,就像VS、VC。这些软件屏蔽了底层的细节,使用户可以不需要关注这些繁琐复杂的东西,只需要关注程序本身即可,但是却淡化了我们对程序的演变和形成
- 这些软件通常是包含了编辑、编译、链接、调试这些功能
1️⃣ 对于编辑功能来说有【编辑器】
2️⃣对于编译功能来说有【编译器】,VS2019为cl.exe
3️⃣对于链接功能来说有【链接器】,VS2019为link.exe
4️⃣对于调试功能来说有【调试器】
3、如何使用gcc
讲了这么多,要如何去使用gcc呢?来看看吧👀
格式: gcc [选项] 要编译的文件 [选项] [目标文件]
具体如何使用,请看下一模块👇
二、✅gcc逐步分析程序的翻译环境
- 下面是从源文件【.c】到可执行文件【.exe】的整体过程
🧐1、预编译【进行宏替换】
在预编译阶段会执行以下四件事:
1️⃣ 头文件的展开 —— 编译器第一件做的事
2️⃣宏定义的替换
3️⃣去注释
4️⃣条件编译的执行
🗯️预编译阶段的核心工作
预编译阶段由预处理器完成,主要执行四项关键操作,且这些操作均为文本层面的处理(不涉及语法检查):
1️⃣头文件展开:
将#include指令引用的头文件(如stdio.h、stdlib.h)内容完整插入到源文件中,替换#include语句。
若头文件中还包含其他头文件,会递归展开,最终形成一个包含所有依赖代码的完整文本。
2️⃣宏定义替换:
对#define定义的宏进行全文替换,例如#define PI 3.14会将代码中所有PI替换为3.14。
带参数的宏(如#define MAX(a,b) (a>b?a:b))也会按定义规则进行文本替换。
3️⃣去除注释
删掉源代码中所有注释(//单行注释或/* */多行注释),注释内容不会出现在预处理后的文件中。
4️⃣条件编译处理
根据#if、#ifdef、#ifndef、#else、#elif、#endif等条件编译指令,保留满足条件的代码块,删除不满足条件的代码。
例如:
#define DEBUG 1 #if DEBUGprintf("调试信息\n"); // 会被保留 #else// 这部分代码会被删除 #endif
🗯️GCC 预编译相关指令 GCC 编译器通过-E选项控制预编译过程
具体用法如下:
1.基础指令:
gcc -E 源文件.c -o 输出文件.i
-E
:指定只进行预编译,完成后立即停止,不进行后续的编译、汇编和链接。-o
:指定输出文件的名称(必须紧跟文件名),通常用.i
作为预处理后文件的扩展名(约定俗成)
.c
结尾的文件表示源程序;.i
结尾的文件表示已经过预处理的C原始程序
2.预处理文件的特点
.i
文件是纯文本文件,可直接用文本编辑器打开查看。- 内容包含展开后的头文件、宏替换后的代码、去除注释后的有效代码,是后续编译阶段的输入。
🧐2、编译【C语言——>汇编语言】
在编译阶段会执行以下四件事:
1️⃣ 语法分析
2️⃣词法分析
3️⃣语义分析
4️⃣符号汇总
编译阶段是 C 语言编译流程的第二阶段,主要将预处理后的文件(
.i
文件)转换为汇编语言代码(.s
文件)。这一阶段通过一系列复杂的分析过程,将高级语言转换为更接近机器语言的汇编指令。以下是对编译阶段核心工作的详细解析🧐:
🗯️编译阶段的四大核心任务
词法分析(Lexical Analysis)
- 作用:将源代码的字符流分割为有意义的 “词法单元”(Token),如关键字(
int
、if
)、标识符(变量名、函数名)、常量(123
、"abc"
)、运算符(+
、==
)、分隔符(;
、{}
)等。- 举例:代码
int a = 10 + b;
会被拆分为int
、a
、=
、10
、+
、b
、;
等 Token。- 目的:过滤无效字符,为后续分析提供结构化的基础单元。
语法分析(Syntax Analysis)
- 作用:根据 C 语言的语法规则(如表达式、语句、函数结构等),将词法单元组合成抽象语法树(AST,Abstract Syntax Tree)。
- 检查内容:验证代码结构是否符合语法规范,例如括号是否匹配、语句是否以分号结束、函数调用参数是否正确等。
- 举例:
a + b * c
会被解析为一棵以+
为根节点,左子树为a
、右子树为b * c
的语法树。- 结果:若存在语法错误(如缺少分号、括号不匹配),编译器会在此阶段报错。
语义分析(Semantic Analysis)
- 作用:对语法树进行逻辑检查,确保代码的 “含义” 合法,不违反 C 语言的语义规则。
- 检查内容:
- 变量是否声明后使用(未声明变量报错);
- 数据类型是否匹配(如 int 变量与 float 变量直接相加是否合法);
- 函数调用是否符合声明(参数数量、类型是否匹配);
- 访问权限是否合法(如对只读变量赋值)等。
- 特点:语义分析不直接生成代码,而是为后续优化和代码生成提供更精确的信息。
符号汇总(Symbol Collection)
- 作用:收集代码中所有的符号(变量名、函数名、数组名等),记录其类型、作用域、存储类别(如全局、局部、静态)等信息,形成符号表(Symbol Table)。
- 用途:符号表是后续汇编、链接阶段的关键依据,用于确定变量 / 函数的地址、检查重复定义等。
- 举例:全局变量
g_var
和函数func()
会被记录在符号表中,注明其类型和作用域。
🗯️编译阶段的输出与 GCC 指令
- 输出文件:编译阶段的结果是汇编语言文件,通常以
.s
为扩展名(如main.s
),包含与源代码对应的汇编指令。- GCC 指令:使用
-S
选项指定只进行编译(到汇编阶段为止),语法如下:gcc -S 预处理文件.i -o 汇编文件.s
示例:
gcc -S file.i -o file.s # 将file.i编译为file.s汇编文件
-S
表示只进行编译而不进行汇编,生成汇编代码.i
结尾的文件表示源程序;.s
结尾的文件表示经过编译生成的汇编代码
总结
编译阶段是 C 语言从高级语言向低级语言转换的核心环节,通过词法分析、语法分析、语义分析确保代码的语法和逻辑正确性,再通过符号汇总构建符号表,最终生成汇编语言代码。这一阶段的输出(
.s
文件)是后续汇编阶段的输入,也是高级语言与机器语言之间的重要桥梁。
🧐3、汇编【汇编语言——>可重定位目标二进制文件】
在汇编阶段会执行以下两件事
1️⃣将汇编指令转换为二进制指令【需要特定的文本阅读器readelf】
2️⃣ 形成符号表
汇编阶段是 C 语言编译流程的第三阶段,主要将汇编语言文件(
.s
文件)转换为机器可识别的二进制指令,并生成可重定位的目标文件(.o
文件或.obj
文件)。这一阶段是连接汇编语言与机器语言的关键环节。以下是对汇编阶段的详细解析🧐:
🗯️汇编阶段的核心任务
1️⃣将汇编指令转换为二进制机器码
- 作用:汇编器(Assembler)会逐条翻译汇编文件(
.s
)中的汇编指令,将其转换为对应 CPU 架构的二进制机器码(0 和 1 组成的指令)。- 特点:每种 CPU 架构(如 x86、ARM)都有其独特的汇编指令集和对应的机器码,汇编器会根据目标架构生成适配的二进制指令。
- 查看方式:生成的二进制目标文件(
.o
)无法直接用文本编辑器查看,需使用readelf
(Linux)、objdump
等工具分析,例如:readelf -h main.o # 查看目标文件的头部信息 objdump -d main.o # 反汇编目标文件,查看二进制指令对应的汇编代码
2️⃣形成并完善符号表
- 作用:汇编阶段会在编译阶段符号汇总的基础上,进一步完善符号表(Symbol Table),记录每个符号(变量、函数等)的具体地址偏移、类型、绑定属性(如全局、局部)等信息。
- 特点:此时的符号地址是 “相对地址”(相对于当前目标文件的偏移量),而非最终的内存绝对地址(需在链接阶段确定),因此称为 “可重定位目标文件”。
- 用途:符号表为后续的链接阶段提供关键信息,用于解决跨文件的符号引用(如调用其他文件中的函数)。
🗯️汇编阶段的输出与 GCC 指令
- 输出文件:汇编阶段生成的是可重定位目标文件,Linux 下以
.o
为扩展名(如main.o
),Windows 下通常为.obj
。该文件是二进制格式,包含机器码、符号表、重定位信息等。- GCC 指令:使用
-c
选项指定只进行汇编(到目标文件阶段为止),语法如下:gcc -c 汇编文件.s -o 目标文件.o
示例:
gcc -c file.s -o file.o # 将file.s汇编为main.o可重定位目标文件
-c
表示是gcc编译到目标代码.s
结尾的文件表示经过编译生成的汇编代码;.o
结尾的文件表示经过编译生成的汇编代码
- 在汇编这一块,就是将汇编语言翻译成可以重定位的二进制文件,里面的二进制代码我们人眼是看不到的,要特定的阅读器才可以看得明白
- 有几个源文件【.c】就会生成几个目标文件【.o】
此时我们可以来试试执行一下这个目标文件,不过发现没有【x】可执行的权限,于是使用
chmod
做一个提权的操作。但是系统却说cannot execute binary file
,上面看到过这是一个二进制文件,对于二进制文件来说是不可以被执行的
可重定位目标文件的特点
- 二进制格式:内容为机器码,无法直接执行(缺少完整的地址信息)。
- 可重定位性:文件中的符号地址是相对偏移,可在链接阶段被调整为最终的内存地址。
- 包含辅助信息:除机器码外,还包含符号表、重定位表(记录需要在链接时修正地址的位置)、段表(代码段、数据段等划分)等。
总结
汇编阶段通过将汇编指令翻译为二进制机器码,实现了从人类可读的汇编语言到机器可执行指令的转换,并通过完善符号表为后续链接阶段奠定基础。生成的可重定位目标文件(
.o
)是编译流程中接近最终可执行文件的中间产物,但其本身还不能直接运行,需要经过链接阶段才能形成完整的可执行程序。
🧐4、链接【生成可执行文件或库文件】
在链接阶段会执行以下两件事
1️⃣ 合并段表
2️⃣ 符号表的合并和重定位
链接阶段是 C 语言编译流程的最后阶段,主要将多个可重定位目标文件(
.o
)及所需的库文件组合,最终生成可直接执行的程序(或库文件)。这一阶段解决了跨文件的符号引用问题,确定所有符号的最终内存地址,使程序能够被操作系统加载执行。以下是对链接阶段的详细解析❓:🗯️链接阶段的核心任务
1️⃣合并段表(Section Merging)
- 背景:每个可重定位目标文件(
.o
)内部按功能划分为不同的 “段”(Section),例如:
.text
段:存放程序的二进制指令(代码);.data
段:存放已初始化的全局变量和静态变量;.bss
段:存放未初始化的全局变量和静态变量(仅记录大小,不占用实际磁盘空间);- 其他段:如
.rodata
(只读数据)、.symtab
(符号表)等。- 作用:链接器会将所有输入目标文件中同名的段进行合并(例如将所有
.text
段合并为一个统一的.text
段,.data
段同理),形成最终可执行文件的段结构。- 目的:统一管理代码和数据,为后续内存加载提供连续的地址空间。
2️⃣符号表的合并和重定位(Symbol Resolution & Relocation)
这是链接阶段最核心的工作,分为两个步骤:
符号表合并:收集所有目标文件的符号表,合并为一个全局符号表,并解决符号引用的一致性:
- 确保全局符号(如函数、全局变量)不被重复定义(重复定义会报错);
- 确保所有符号引用都能找到对应的定义(未定义的符号会报错,如调用了未实现的函数)。
重定位:为每个符号分配最终的内存地址,并修正所有代码和数据中对符号的引用(将之前的相对偏移量更新为绝对地址):
- 例如,目标文件 A 中调用了目标文件 B 的
func()
函数,汇编阶段仅记录了func()
的相对偏移,链接阶段会确定func()
在最终可执行文件中的绝对地址,并修正 A 中调用func()
的指令地址。- 重定位依赖于目标文件中的 “重定位表”(
.rel.text
、.rel.data
等),该表记录了需要修正地址的位置和对应符号。🗯️链接阶段的输出与 GCC 指令
输出文件:
- 可执行文件:在 Linux 下通常为无扩展名的文件(如
main
),Windows 下为.exe
文件;- 库文件:若指定生成库(静态库
.a
或动态库.so
),则输出相应的库文件。GCC 指令:直接将目标文件(
.o
)作为输入,默认生成可执行文件,语法如下:gcc 目标文件1.o 目标文件2.o ... -o 可执行文件名
示例:
gcc -E file.c -o file.i # 将file.o链接为可执行文件app
-E
表示是让 gcc 在预处理结束后停止编译过程;-o
表示输出到指定文件.c
表示源程序;.i
表示已经过预处理的C原始程序
- 这里便不展示与其余文件的合并了,这里导入一个之前做过的GIF
- 可以来看看这个可执行文件
my_out
,它也是一个内容也是二进制代码,可供机器识别
链接的两种方式
静态链接:
- 链接时将库文件的代码完整复制到可执行文件中,生成的可执行文件体积较大,但运行时不依赖外部库。
- 静态库在 Linux 下为
.a
文件,Windows 下为.lib
文件。动态链接:
- 链接时仅记录库文件的引用信息,不复制代码,生成的可执行文件体积较小,但运行时需要依赖系统中的动态库(
.so
或.dll
)。- 动态库可被多个程序共享,节省内存和磁盘空间。
🗯️总结
链接阶段通过合并段表统一程序结构,通过符号表合并和重定位解决跨文件符号引用问题,最终生成可直接执行的程序。这一阶段是将分散的目标文件 “组装” 为完整程序的关键步骤,确保了程序中所有代码和数据的地址一致性和可访问性。
🌟完整编译流程总结
一、完整编译流程概览
C 语言代码从编写到执行需经历预编译→编译→汇编→链接四个阶段,最终生成可执行文件。每个阶段由不同的工具(预处理器、编译器、汇编器、链接器)处理,逐步将高级语言转换为机器可执行的指令。流程如下:
源文件(.c)→ 预处理文件(.i)→ 汇编文件(.s)→ 目标文件(.o)→ 可执行文件
二、各阶段核心任务与输出
预编译阶段(Preprocessing)
- 工具:预处理器(cpp)
- 输入:源代码文件(.c)
- 核心操作:
- 头文件展开(将
#include
的文件内容插入源文件)- 宏定义替换(替换
#define
定义的宏)- 去除注释(删除
//
和/* */
注释)- 条件编译处理(根据
#if
等指令保留有效代码)- 输出:预处理文件(.i),纯文本格式,包含展开后的完整代码。
- GCC 指令:
gcc -E main.c -o main.i
编译阶段(Compilation)
- 工具:编译器(cc1)
- 输入:预处理文件(.i)
- 核心操作:
- 词法分析(将代码拆分为关键字、标识符等词法单元)
- 语法分析(验证代码结构,生成抽象语法树)
- 语义分析(检查逻辑合法性,如变量未声明、类型不匹配)
- 符号汇总(收集变量、函数等符号,形成初步符号表)
- 输出:汇编语言文件(.s),包含与源代码对应的汇编指令。
- GCC 指令:
gcc -S main.i -o main.s
汇编阶段(Assembly)
- 工具:汇编器(as)
- 输入:汇编文件(.s)
- 核心操作:
- 将汇编指令转换为对应 CPU 架构的二进制机器码
- 完善符号表(记录符号的相对地址、类型等信息)
- 输出:可重定位目标文件(.o),二进制格式,包含机器码和符号表。
- GCC 指令:
gcc -c main.s -o main.o
- 查看工具:
readelf
、objdump
(分析二进制内容)
链接阶段(Linking)
- 工具:链接器(ld)
- 输入:多个目标文件(.o)及库文件
- 核心操作:
- 合并段表(将所有目标文件的
.text
、.data
等段合并)- 符号表合并与重定位(解决跨文件符号引用,确定符号最终地址)
- 输出:可执行文件(或库文件),可被操作系统直接加载运行。
- GCC 指令:
gcc main.o util.o -o app
(生成可执行文件app
)- 链接方式:
- 静态链接:将库代码复制到可执行文件中,不依赖外部库
- 动态链接:仅记录库引用,运行时依赖动态库(.so/.dll)
三、关键特点与作用
- 逐步转换:从人类可读的 C 语言,逐步转换为机器可执行的二进制指令,每阶段都为下一阶段提供更底层的输入。
- 错误检查:编译阶段(语法、语义分析)是主要的错误检查环节,会报出语法错误(如缺少分号)、逻辑错误(如未声明变量)等。
- 符号管理:符号表贯穿编译流程,从汇总到完善再到合并,最终确保程序中所有变量、函数的地址正确且可访问。
- 灵活性:通过动态链接实现库文件共享,减少可执行文件体积;通过静态链接确保程序在无依赖环境下运行。
四、总结
C 语言的编译流程是一个 “分层处理、逐步转换” 的过程,每个阶段专注于特定任务:预编译处理文本、编译进行语言分析、汇编生成机器码、链接组装为完整程序。理解这一流程有助于开发者调试代码(如定位宏替换问题、符号未定义错误)、优化程序性能,并深入掌握程序的执行机制
5、巧记gcc命令选项与生成文件后缀【⭐】
📚 命令选项:
-E
-S
-c
【键盘左上角的Esc,不过中间的S要大写】
📚 文件后缀:.i
.s
.o
【.iso为镜像文件的后缀,ISO也为国际标准化组织】
三、✅gcc与g++的区别
看了这么久gcc,我们来看看g++,其实这两个编译器的用法是一模一样的,只是对于gcc才说是用来编译C语言代码,对于g++来说是用来编译C++的代码
1️⃣基础共性:用法框架一致
二者均遵循
[编译器] [选项] 源文件 [选项] 目标文件
的命令格式,例如编译单个源文件生成可执行程序时,gcc test.c -o test
与g++ test.cc -o test
的语法逻辑完全相同,底层均依赖 GNU 编译体系的预编译、编译、汇编、链接流程。2️⃣核心差异:编译场景与细节
对比维度 gcc 编译器 g++ 编译器 默认编译语言 默认用于编译 C 语言(源文件后缀 .c
)默认用于编译 C++ 语言(源文件后缀 .cc
/.cpp
/.cxx
)C++ 代码编译能力 可编译 C++ 代码,但需手动处理两个关键问题:
1. 需通过
-lstdc++
参数手动链接 C++ 标准库;2. 语法检查相对宽松(例如对 C++ 专属语法的兼容性较弱)
天然支持 C++ 编译:1. 自动引入并链接 C++ 标准库(无需额外参数);
2. 严格遵循 C++ 语法规则(例如强制检查类、模板、命名空间等专属语法)
C 语言代码编译能力 对 C 语言语法支持更精准(例如兼容 C99/C11 等标准的特性) 可编译 C 语言代码,但会按 C++ 语法规则进行检查(例如不允许 C 语言的非标准语法,如隐式类型转换的宽松限制) 使用便捷性(C++ 场景) 繁琐:编译 C++ 时需额外添加 -lstdc++
等参数,对新手不友好简化:一键完成 C++ 代码编译与标准库链接,降低使用门槛 3️⃣关键结论:选择逻辑
- 编译 C 语言程序:优先用
gcc
,语法支持更精准,避免 C++ 编译器的严格检查对 C 语言非标准写法的 “过度限制”。- 编译 C++ 程序:优先用
g++
,无需手动处理标准库链接,且语法检查更符合 C++ 规范,减少编译配置成本。- 特殊场景:若需用
gcc
编译 C++(例如兼容特定编译脚本),需补充命令参数,示例:编译 C++ 源文件test.cpp
并生成可执行程序,需执行gcc test.cpp -o test -lstdc++
(-lstdc++
指定链接 C++ 标准库)。简言之,二者并非 “专属语言编译器”,而是
g++
在gcc
基础上针对 C++ 场景做了 “简化优化”—— 自动适配 C++ 标准库与语法规则,让开发者无需关注底层链接细节,更适合 C++ 开发;而gcc
在 C 语言场景下的兼容性与精准度更优。
四、🗯️谈谈链接的过程【动静态库的理解】
在上面,我们说到了对于一个程序而言分为四步,预编译 --> 编译 --> 汇编 --> 链接。对于前面三步而言我给出了对应的记忆方法。但是对于最后一步的链接,却不是那么好理解,所以我们专门来谈谈这个链接🔗
1、 库的初步认识
首先要说一些必不可少的前言小知识
❓问:我们为什么能够在Linux在进行C、C++代码的编写和编译呢?
对于上面这个问题相信你也是非常得困惑,在上面的演示过程中,我也往
file.c
里写了一些C语言的代码,其实你把后缀改为.cc
就可以写一些C++的代码了
- 这究竟是为何呢?答:因为Linux大部分都是用C写的,少量使用汇编,而C++则是兼容C,所以在Linux系统默认已经携带了语言级别的头文件和语言对应的库!
- 可以先来看看系统中的头文件所在目录,然后就可以看到我们熟悉的
stdio.h
、stdlib.h
等等
- 然后在这些头文件中包含了对应的库函数实现,我们可以进去看看
- 然后在里面就可以看到我们在C语言中使用的
printf()
和scanf()
这些库函数,这下你应该知道为什么我们在代码的时候引入stdio.h
这个头文件就可以使用里面的库函数了
所以可以得出一个结论:只要有头文件就必有源文件
- 其实我们在安装VS2019、VS2022的时候。最重要的一个工作是什么呢?其实在这么几十分钟的安装时间里,这个自动化程序更多的是在帮我们下载并安装语言的头文件和库文件【编译需要的两个东西】
📖拓展【ldd】指令
接下去我们来介绍一个指令,因为在后面的讲解中会使用到
格式:ldd filename —— 检测可执行程序形成的时候都依赖了哪些库
1️⃣l -> list 【列出】
2️⃣
d -> dynamic 【动态的】
3️⃣
d -> dependencies【依赖关系】
4️⃣
——> 列出动态依赖关系
- 然后我们就可以去看看每次gcc最终链接后形成的
a.out
这个可执行程序都依赖了哪些库。然后可以看到对于我框起来的这个就是标准的C语言库
解释:
- => 左边的是程序需要连接的共享库
- => 右边的是由 Linux 的共享库系统找到的对应的共享库在文件系统中的具体位置
知道了这些其实就可以回答上一个小节我所提出的问题了🔍
【最后的答案是】:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是⭐链接⭐的作用
❓这样就又衍生出了一个问题:
Linux中的这些指令从何而来,为什么使用这些指令可以完成对应的操作❓
接下去我列举几个常见的指令:
- 通过列出上面三个指令所依赖的动态库,相信你应该知道为什么这些指令完成它们对应的操作,也可以回答出我提的这个问题
【最后的答案是】:程序、工具、指令是一回事【都是用C语言写的,都是经过编译形成的,都和C语言标准库有关】
2、 动态库与静态库
看完了上面这些,对Linux中的库有了一个基本概念后,我们便可以来谈谈【动态库】与【静态库】
- 因为对于动静态库而言Windows也是有的,所以我会做一个对比
一、基本概念
1️⃣静态库:编译时被直接链接到目标程序中的库,生成的可执行文件包含完整的库代码。
- Linux下:以
lib
作为前缀,.a
作为后缀【libXXXXXX.so】
上面的这些是在
/lib64
下找到的
- Windows下:以
.lib
作为后缀
2️⃣动态库:运行时由操作系统动态加载到内存的库,多个程序可共享同一库实例。
- Linux下:以
lib
作为前缀,.so
作为后缀【libXXXXXX.so】
- Windows下:以
.dll
作为后缀
二、本质区别与核心原理
静态库(Static Library)
- 本质:编译阶段被完整复制到可执行文件中的二进制代码集合
- 原理:链接器在编译时将库中被引用的代码段直接嵌入目标程序,形成单一可执行文件
- 特征:程序运行时不依赖外部文件,自给自足
动态库(Dynamic Library)
- 本质:独立于可执行文件的代码模块,运行时按需加载
- 原理:编译时仅记录库引用信息,运行时由操作系统加载器载入内存并完成地址重定位
- 特征:多个程序可共享同一份库实例,实现内存资源复用
三、技术特性对比
维度 静态库 动态库 文件扩展名 Windows:.lib; Linux/macOS:.a Windows:.dll; Linux:.so; macOS:.dylib 链接时机 编译期(链接阶段) 运行期(加载 / 执行阶段) 可执行文件体积 较大(包含库代码) 较小(仅包含引用信息) 内存占用 多个程序使用时存在多份副本 共享一份内存副本 更新维护 需重新编译整个程序 可独立更新,无需重新编译程序 分发部署 单一文件,部署简单 需同时分发库文件,版本需匹配 加载速度 启动快(无需动态解析) 启动稍慢(需加载和重定位) 符号冲突风险 较高(全局符号合并) 较低(独立命名空间)
四、开发实践考量
静态库实践要点
- 会增加编译时间(每次都需处理完整库代码)
- 需注意不同编译选项可能导致的兼容性问题
- 适合功能稳定、变更频率低的代码模块
动态库实践要点
- 需要处理版本兼容问题(建议采用语义化版本)
- 需设计稳定的接口(避免接口变更影响依赖程序)
- 跨平台开发时需注意不同系统的动态库机制差异
- 可通过延迟加载优化程序启动性能
五、总结
静态库与动态库的选择本质上是空间与时间、独立与共享的权衡:
- 追求部署简单性和运行独立性时选择静态库
- 注重资源效率和更新灵活性时选择动态库
现代软件开发中,通常采用混合策略:核心稳定功能使用静态库保证可靠性,频繁更新或多程序共享的功能使用动态库提升效率。
但是光这么看动静态库就非常晦涩难懂了,不用急,在下一模块,我将用更加形象的方式来介绍它们
3、动静态库的感性理解【小蚂蚁网吧🐜】
故事背景:你是一个初三的学生,在学校被老师管,在家里又被父母管,于是你就想着在上了高中之后要住校,然后就可以无拘无束地玩了,你呢,很喜欢玩游戏,于是就找到你心仪哪所高中的学长询问附近有没有对应的网吧,提前做好打听;
一年后你也考上了自己心仪的高中,终于可以挣脱父母的管教了
- 在高中的学校待了两个多礼拜之后呢,来到了周末,老师也布置了很多的作业,所以你本来想要去。你呢前一天晚上躺在床上就想自己原本要出去网吧玩的计划可能很难实现,因为作业比较多,于是呢就给自己列了一个清单,明天要完成哪些学习任务
- 然后到了第二天早上八点,你起来了,要先做一小时数学作业,然后写会作文,叫做《我的父亲👨》,因为文笔不是很好,所以可能需要写两个小时,那这个时候来到了中午,要吃饭了,你呢可以点外卖可以不用去食堂,可以边吃外卖🍚边看看教学视频📺,然后在吃完饭后就练习练习配化学方程式。
- 然后这个时候时间来到了下午两点,室友都在午休,然后你呢就想着也写了一上午+中午的作业了,应该要休息一下,做到“劳逸结合”,于是呢就顺序着自己的计划找到了学校的北门,恰好保安也没有在岗,你就溜了出去,此时呢就看到你学长和你说的 #小蚂蚁网吧🐜#,于是就进去找到网管,网管给你分配了一个座位,3块钱一个小时,你买了两个小时,然后就开始自己疯狂的游戏💻
- 然后两个小时到了之后,你只能忍痛割爱离开你的游戏o(╥﹏╥)o
🎯 先戳透第一个关键:程序啥时候 “自己玩不转”?就像你写作业到 “上网” 那步一样❓
- 我们可以将这个网吧当做的是系统中的库,然后把你所列的这个清单当做是一段程序。做数学题”“写《我的父亲》”“配化学方程式”,都是你自己能搞定的事 —— 就像程序里的 “简单循环”“条件判断”“自己写的小算法”,代码量少、逻辑直接,不用麻烦别人。
- 但到了 “上网玩游戏” 这步,你自己搞不定了:你没有电脑,没有游戏安装包,更没有能联网玩的环境 —— 这就像程序里遇到 “复杂逻辑” 时的困境:比如想做 “人脸识别”,要处理图像像素、比对特征值;想做 “网络请求”,要懂 TCP/IP 协议、解析数据包;想做 “数据加密”,要写复杂的加密算法…… 这些逻辑不是 “写几行循环” 能搞定的,得靠大量成熟的代码才能实现。
- 而 “小蚂蚁网吧” 就像现成的库函数:它帮你把 “玩游戏需要的设备(电脑)、环境(网络)、资源(游戏安装包)” 全备好,你不用自己造电脑、装游戏 —— 库函数也一样,把 “复杂逻辑” 提前写好、打包好,程序不用自己从零写,直接调用就行。
🎯 再讲第二个核心:你怎么找到 “网吧”?学长指路 = 程序找库的 “地址”❓
你一开始不知道网吧在哪,是学长告诉你 “学校北门附近有小蚂蚁网吧”—— 这步太关键了,就像程序要调用库函数,必须先知道 “这个函数存在哪”。
- 要是调用静态库,就像你把学长指的路 “记死在脑子里”,出门直接按这个路线走,到了网吧还把 “电脑、游戏” 全搬回自己家(编译时把库代码完整复制到程序里)—— 之后哪怕学长忘了路,你也能自己玩,因为 “资源全在自己这”。
- 要是调用动态库,就像你只记着 “北门小蚂蚁网吧” 这个 “地址”,出门时才按地址去找网吧,用的还是网吧里的公共电脑(运行时才去系统里找库文件、加载到内存)—— 这时候 “地址不能错”,要是学长指错路(库地址不对),或者网吧没开门(库文件缺失),你就玩不成,程序也会报错。
🎯最后补个小细节:为啥不自己 “造网吧”?就像程序为啥不自己写复杂逻辑❓
- 你要是不想去网吧,想自己搞一套能玩游戏的设备,得买电脑、装系统、下游戏、拉网线 —— 花时间又费钱,还容易出问题(比如电脑坏了、游戏装不上)。
- 程序也一样:要是不调用库,自己写 “人脸识别”“网络请求” 的代码,不仅要写几万行甚至几十万行代码,还要反复调试 bug(比如协议解析错了、算法效率低),最后可能还不如现成的库好用。库函数就像 “现成的解决方案”,是无数工程师调试优化过的,既靠谱又省时间 —— 就像网吧是专业做 “提供上网服务” 的,比你自己折腾设备靠谱多了。
总结下来就是:
- 程序 “自己能搞定的小事”= 你写作业里的数学题、作文;
- 程序 “搞不定的大事”= 你需要上网玩游戏(缺设备 / 环境);
- 库函数 = 小蚂蚁网吧(备好现成资源,帮你解决难题);
- 学长指路 = 程序找库的 “地址”(知道去哪调用,才能用得上库)。
🎯学长(链接器)到底帮你做了什么?—— 对应 “链接器的核心作用”❓
- 那这么来说你的学长就相当于是一个链接器,网吧就是一个动态库,你知道了动态库中所需要的这个函数的地址,然后你就顺藤摸瓜🍈地找到了这个地方,你的学长就做了一个链接的功能
- 你一开始只知道 “想上网”,但不知道 “网吧在哪、怎么去”
- 这时候学长告诉你:“从学校北门溜出去,右转 50 米就是小蚂蚁网吧”—— 相当于链接器帮你做了两件关键的事:
- 第一步:找地址:确认 “图片压缩函数” 在 “某某动态库” 里,并且找到这个库在系统中的位置(就像网吧的具体地址);
- 第二步:建通道:告诉你 “到了网吧后,直接用 3 号机,游戏已经装好了”—— 相当于在你的代码(目标文件)里,添加一个 “指向库函数地址的链接”,等程序运行时,就能顺着这个 “链接” 找到库函数。
- 要是没有学长(链接器),你就算想上网,也不知道去哪找网吧;同理,没有链接器,代码就算写了 “调用库函数”,也找不到对应的库,根本跑不起来。
🎯“你(目标文件)” 怎么变成 “能玩游戏的你(可执行程序)”?—— 对应 “目标文件到可执行程序的转化”
- 你刚写完作业时,只是 “完成了部分任务的你”—— 手里有清单(程序逻辑),但没找到网吧(没链接库),还不能实现 “玩游戏” 的核心功能,这时候的你就是 “目标文件”:有代码,但不完整,不能直接 “运行”(玩游戏)。
- 直到你顺着学长指的路,找到网吧、坐在电脑前 —— 这时候的你,既完成了作业(自定义代码),又能调用网吧的设备(库函数),终于能实现 “玩游戏” 的完整任务,这才变成了 “可执行程序”:整合了所有逻辑,能直接执行最终功能。
- 而且这里要注意:你并没有 “把网吧搬回自己家”(没把库代码复制到自己身上),只是 “去网吧用设备”—— 这正是动态库的特点:程序运行时才去 “找库、用库”,不把库代码嵌入自己的可执行文件里。
- 还记得我在一开始放出的那张图吗,你就可以把自己想做是目标文件。那个可执行程序也就是最后你可以打游戏的那台机子。你在打游戏的这个过程其实就相当于是在调用库函数的一个过程了。
🎯 “玩完游戏回学校”—— 对应 “库调用结束,程序继续执行”
- 你在网吧玩了两个小时,到点后离开网吧、回学校 —— 这相当于程序 “调用库函数” 的过程结束:库函数帮你完成了 “图片压缩”(玩游戏)的功能后,就把 “控制权” 还给你的程序,你回到学校后还能继续做其他事(比如接着写剩下的化学作业),程序也能继续执行后续的代码逻辑。
- 要是换成静态库呢?场景就变了:你不是去网吧玩,而是 “把网吧的电脑买回家、装在自己房间里”—— 写代码时,直接把库函数的代码完整复制到自己的程序里,运行时不用再找外部的库(不用去网吧),但自己的 “设备”(可执行文件)会变重(占更多空间),而且要是想更新游戏(库函数),得把整个电脑里的游戏重新装一遍(重新编译程序)。
- 最后再总结一下这个 “故事逻辑链”,保证你下次看到动静态库就想起这段经历:
清单(程序)→ 写作业(自定义代码)→ 找学长问地址(链接器找库)→ 去网吧玩游戏(调用动态库)→ 回学校(库调用结束)
核心区别就一句话:动态库是 “去网吧用公共电脑”(共享、依赖环境),静态库是 “买电脑放家里自己用”(独立、占空间) —— 是不是一下子就通了!
但是事情完了嘛,当然没有,才刚刚开始。。。
- 你们学校呢也有很多同学喜欢玩游戏,也通过他们各自的学长知道了这个【小蚂蚁网吧】,于是周末也来玩,但是呢这个网吧似乎不太安全,突然警察👮造访,说他们涉嫌非法经营🈲,然后这个网吧就被封了,你和你的同校同学都玩不了,知道默默回学校
🎯上面有说到,这个是网吧就是动态库,我们在上面有说到过这个很多指令都在使用这个C语言的标准动态库
🎯这个警局你可以认为是恶意入侵的黑客,然后将你系统的库搞坏了,那网吧中的点电脑就可以算作是指令,若是库没了,那你的指令就无法执行了,所以我们不要去随便动系统中的已经写好的库,否则你的Linux就无法正常使用了🎯为什么 “动态库一坏,所有指令都没法执行”?—— 对应 “动态库的共享依赖性”❓
- 你想想:小蚂蚁网吧里有 10 台电脑,不管是玩《英雄联盟》还是《CS》,都得在网吧里用这些电脑 —— 要是网吧被警局查封了(动态库坏了),哪怕你带了鼠标键盘(自己的代码),也没电脑能玩(没法执行指令)。
- 这和 Linux 系统里的动态库完全一样:Linux 里的
ls
(查看文件)、cd
(切换目录)、gcc
(编译代码)这些常用指令,本质都是 “依赖 C 标准动态库(libc.so)才能跑的程序”。就像所有上网的人都依赖网吧,所有这些指令都依赖同一个动态库 ——动态库是 “共享资源”,一个库支撑成百上千个程序 / 指令。- 所以一旦这个库被黑客破坏(比如删除、篡改),就会出现 “牵一发而动全身” 的后果:不是某个指令用不了,而是所有依赖它的指令全失效—— 你在终端里敲
ls
没反应,敲cd
也没反应,最后整个 Linux 系统都没法正常用,就像 “所有想去上网的人都被断了路”。🎯为什么说动态库 “不安全”?—— 对应 “共享带来的风险集中性”❓
- 静态库就像 “你自己买了台电脑放家里”,就算网吧被查封了(系统库坏了),你照样能在家玩游戏(程序能运行)—— 因为静态库在编译时已经把 “游戏代码(库代码)” 复制到你自己的程序里,不依赖外部的库。
- 但动态库是 “大家共用一个网吧”:好处是省资源(不用每个人都买电脑),但坏处是 “风险集中”—— 只要网吧出问题(动态库坏了),所有人都受影响。比如黑客篡改了 C 标准动态库,在里面加了一段 “偷数据” 的代码,那么所有依赖这个库的程序(从简单的
ls
指令,到复杂的浏览器),在运行时都会执行这段恶意代码 —— 就像网吧里的电脑被装了病毒,不管谁来玩,都会中招。- 这就是动态库 “不安全” 的核心:共享意味着风险共享,一个库的漏洞或破坏,会扩散到所有依赖它的程序。
🎯为什么 “不要随便动系统库”?—— 对应 “系统库是 Linux 的‘命根子’”❓
你去网吧上网,肯定不会随便拆网吧的电脑、删游戏文件 —— 因为你知道一搞坏,不仅自己玩不了,别人也玩不了。Linux 系统里的系统库(比如 libc.so、libpthread.so)就是 “网吧的核心设备”,是整个系统正常运行的 “命根子”。
这些系统库不是 “你自己写的库”,而是 Linux 系统预装的、支撑所有基础功能的库:
- 你安装的软件(如 Chrome、VS Code)依赖它;
- 系统自带的指令(如
sudo
、apt
)依赖它;- 甚至系统启动时的很多服务(如网络服务、进程管理)也依赖它。
要是你手贱删了
/lib/x86_64-linux-gnu/libc.so.6
(Ubuntu 里的 C 标准库路径),后果比 “网吧电脑全坏了” 还严重 ——Linux 会直接无法启动,或者启动后所有指令都报错,就像 “整个街区的网吧全被查封,所有人都没法上网,还出不了街区”。最后再用一句话总结这个场景里的动态库逻辑:
动态库是 “共享网吧”,所有程序都靠它 “玩游戏(执行功能)”,但只要网吧被搞坏(库被破坏),所有人都没得玩;系统库是 “城市核心网吧”,动它就等于搞瘫整个城市的上网功能 —— 所以千万别随便碰!
👉因此我们其实可以初步看出来这个动态库其实是不安全的,因为是共享的,所以大家都可以使用,但是只要它被破坏了,那大家都没得玩了
故事还在继续。。。
- 一个学期结束了,你放假回到家里,这次的期末考试你考到了班级第一,虽然你也有在玩游戏,但是你也有自己的学习规划。真正地做到了#劳逸结合#,而且你的作文《我的父亲》获得了校园征文大赛一等奖,这让你的父亲非常得开心,于是就说:“儿子,干的不错。说吧,想要什么都可以满足你”
- 那这个时候你就觉得学校外面的网吧真的不是不太安全,如果我自己有一台电脑就好了😍
- 于是这个时候你就和你爸爸说想买一台电脑——>为了可以更好地学习。那这个时候你爸就觉得也很有道理,于是就答应你了
- 在新学期开学后一个礼拜,此时你正在宿舍学习,然后你爸突然就到进到宿舍然后搬进来一台电脑,接着你仔细看了看这台电脑好熟悉的感觉,Σ(っ °Д °;)っ这不是网吧的电脑💻,于是你爸就说:“儿子,怎么样,这台电脑喜欢吗,我不知道你想买怎样的电脑,于是在学校附近逛的时候就发现了一家【大象网吧】,问了里面的老板电脑的配置,然后直接从他那里买了一台,这种你们年轻人的电脑你应该用的惯吧”
- 于是你就说:“哦哦,谢谢爸爸,可以的。大象网吧(ˉ▽ˉ;)…。。。”
- 于是到了下个周末之后你同学约你一起去网吧,你说:“我都有电脑了,不需要去了”。然后你的同学回来之后就说:“那个大象网吧好像被封了,说是黑网吧”。此时你就笑笑不说话😊心想:封了还更好,这样就不会影响大家学习了。。这样的网吧真的是太没有原则了,毫无底线呀,不过里面的电脑💻是没错的,不要可惜了呀。
🎯可以看到,其实对于从网吧中拿来的电脑,就是库中的那个函数,也就是将库中的整个函数直接放到你的程序中来,这样你完全不需要再到库中去调用函数了,直接在你的程序中使用即可
🎯当你的同学去到黑网吧,然后得知被封了之后无奈返回后,这个时候你有这自己的电脑,完全不需要再去考虑是否有电脑可用的安全性了,但就是这个电脑放在宿舍可能有些占位子,原本比较宽阔的空间就要被一台电脑给霸占了,那确实是没办法🎯“把网吧电脑买回家”—— 对应静态库 “编译时完整复制”❓
之前聊动态库时,你是 “去网吧用公共电脑”—— 玩的时候才临时用,电脑(库)还在网吧(系统目录)里,没真正属于你。但静态库不一样:你直接把网吧的电脑(库函数)完整买回家,连里面装的游戏(库代码)一起搬回宿舍 —— 这就对应静态库的 “静态链接” 过程:
- 编译程序时,编译器会把静态库里的所有需要的代码,像 “搬电脑” 一样完整复制到你的程序里。比如你写代码时调用了静态库里的 “图片压缩函数”,编译后这个函数的代码就成了你程序的一部分,不再需要 “去系统里找库文件”(去网吧找电脑)。
- 就像你在宿舍打开自己的电脑,不用管网吧有没有开门、有没有被封 —— 程序运行时,直接用自己 “带在身上” 的库代码,完全不依赖外部的库文件。这就是静态库最核心的优势:独立性强,安全性高,不怕外部库出问题。
🎯“同学去不成黑网吧,你照样玩”—— 对应静态库 “运行时不依赖外部”❓
- 你同学想去黑网吧(依赖动态库),结果网吧被封了(动态库坏了 / 缺失),只能无奈返回 —— 这就像动态库程序遇到 “库文件被删、版本不兼容” 时,直接报错跑不起来。
- 但你不一样:你有自己的电脑(静态库已嵌入程序),不管外面的网吧(系统里的动态库)出什么问题,哪怕所有网吧都被封了,你在宿舍里照样能打开电脑玩游戏 —— 这就是静态库的 “安全感”:
程序一旦编译完成,就成了 “自给自足” 的整体,里面包含了所有需要的库代码,不需要再找任何外部库文件。比如 Linux 里用静态链接编译的ls
指令,就算系统里的 C 标准动态库(libc.so)被误删了,它照样能正常执行;但动态链接的ls
指令,此时早就报错用不了了。🎯 “电脑占宿舍空间”—— 对应静态库 “程序体积大、占资源”❓
但 “自己有电脑” 也不是没代价:原本宿舍宽阔的空间,被一台电脑占了一块地方 —— 这正好对应静态库的 “空间代价”:
- 因为静态库在编译时会把完整的库代码复制到程序里,不像动态库那样 “多个程序共享一份库”,所以静态库编译出来的程序体积会明显变大。比如一个简单的 “Hello World” 程序,动态链接编译后可能只有几 KB,静态链接后直接涨到几百 KB—— 就像你原本空着的宿舍角落,突然多了一台电脑,占了实实在在的空间。
- 更关键的是:如果你的室友也想玩,他也得买一台一模一样的电脑(另一个静态库程序)—— 宿舍里就会有两台一样的电脑,各自占空间;对应到程序里,就是多个静态库程序同时运行时,会在内存里存多份相同的库代码,浪费内存资源。而动态库呢?大家共用网吧里的一台电脑(内存里一份库代码),宿舍(内存)里就不会挤得慌。
静态库 vs 动态库 —— 再用 “电脑归属” 总结核心区别
到这里,动静态库的区别就完全能用 “电脑在哪” 说清楚了:
对比维度 静态库(自己买电脑) 动态库(去网吧用电脑) 库的 “归属” 程序 “自己拥有”(代码嵌入程序) 程序 “临时借用”(库在系统里) 运行依赖 不依赖外部库(不用去网吧) 依赖系统里的库(网吧得开门) 安全性 高(不怕库坏 / 被封) 低(库坏了就用不了) 空间 / 内存占用 程序体积大、多份用占内存(占宿舍) 程序体积小、多份用共享内存(省空间) 更新成本 要重新买电脑装游戏(重新编译程序) 网吧直接更游戏(更新库就行) 🧐【动态链接】 —— 仅仅是把库中的你所用的方法的地址拷贝到程序里
网吧
- 动态库优点:因为可以做到被大家共享方法,所以真正的实现永远都是在库中,程序内部只有地址,比较节省空间
- 动态库缺点:我们的程序还是依赖任何库,一旦动态库缺失,我们的程序便无法运行
📝可以联想到,对于小蚂蚁网吧🐜来说,大家都可以去玩,只需要知道网吧的地址在哪里,然后直接去即可,也就和函数一样直接进行一个调用,对自己的程序而言不需要消耗任何的空间
📝但是呢一旦这个网吧被封了,也就是动态库丢失了,那此时我们再去调用库中的函数就会失败,这是动态库比较致命的一点😱
🧐【静态链接】 —— 自己代码当中用到的库中的方法直接拷贝到程序里
台式电脑
- 静态库优点:我们的程序不依赖任何库,自己就可以独立运行
- 静态库缺点:因为自身的拷贝问题比较浪费空间
📝对于静态库来说,确实是比较方便,不需要再去库里面调函数了,直接把这个函数整体拷贝到我们的程序中即可。也就是我们不需要依赖任何的库,也不用去大象网吧🐘
📝但是呢这也意味着我们的程序会变得非常庞大,举个最简单的例子就是你完全不写任何自己的函数,所有功能和实现都放在main函数里,这就会显得非常浪费空间做做实验【动静态链接的使用】
- 在[Linux基础——Lesson2.Linux的基本指令使用]中有说到过
file
指令可以用来查看一个文件的类型,那我们就可以来看看这个动态的可执行文件
- 最重要的就是这个
dynamically linked
动态的链接,这也就意味着这个可执行文件是经过动态链接生成的(gcc编译完默认是动态链接)- 那我们要如何使一个文件进行静态链接呢,很简单,只需要在使用gcc编译的时候在后面加上一个
-static
的选项即可
最后一句话帮你记住:
静态库是 “自己的电脑”—— 安全独立,但占地方;动态库是 “网吧的电脑”—— 省空间方便,但怕网吧出问题。下次写代码选库时,就想想 “你是想在宿舍占块地方放电脑,还是想赌网吧今天没被封”,瞬间就能做出选择~
总结
- 在一开始,我们了解了一个程序是如何诞生的,然后初步接触到了Linux中的编译器gcc
- 然后我们使用gcc针对程序的翻译环境进行了逐步展开分析,看到了一个.c的源文件究竟是怎样一步步变成一个.exe可执行程序的,其中要经过【预编译】【编译】【汇编】【链接】四个步骤
- 最后我们对最后一个链接做了详细的介绍,谈到了Linux中库的概念,然后了解到了【动静态库】,我用了一个小故事让你更好地感受什么是动静态库,相信你在看了这个故事后一定也会有所收获。了解了什么是动静态库后就逐步上手使用他们进行动态链接与静态链接。
结束语:
以上是我对于编译器gcc/g++以及动静态库的理解
感谢你的三连支持!!!