Linux开发基础(1):GCC、静态库、动态库
1.1 GCC
01 什么是GCC
-
GCC原名为GNU C语言编译器(GNU C Compiler)
-
GCC (GNU Compiler Collection,GNU编译器套件)是由 GNU 开发的编程语言译器。GNU编译器套件包括C、C++、objective-C、Java、Ada和Go语言前端,也包括了这些语言的库(如libstdc++,libgcj等)
-
GCC不仅支持C的许多“方言”,也可以区别不同的C语言标准;可以使用命令行选项来控制编译器在翻译源代码时应该遵循哪个C标准。例如,当使用命令行参数
-std=c99启动GCC时,编译器支持C99标准。 -
安装命令 sudo apt install gcc g++ (版本 > 4.8.5)
-
查看版本gcc/g++ -v/--version
02 编程语言的发展

03 GCC工作流程

- 预处理工作:把头文件展开,删除注释,宏替换等
04 gcc和g++的区别
-
gcc和g++都是GNU (组织)的一个编译器。
-
误区一:gcc只能编译c代码,g++只能编译c++代码。其实两者都可以,请注意:
- 后缀为.c的,gcc把它当作是C程序,而g++当作是c++程序
- 后缀为.cpp的,两者都会认为是C++程序,C++的语法规则更加严谨一些
- 编译阶段,g++会调用gcc,对于C++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接统统用g++了,这就给人一种错觉,好像cpp程序只能用g++似的
- 误区二:gcc不会定义_cplusplus宏,而g++会
- 实际上,这个宏只是标志着编译器将会把代码按C还是C++语法来解释
- 如上所述,如果后缀为.c,并且采用gcc编译器,则该宏就是未定义的,否则,就是已定义
- 误区三:编译只能用gcc,链接只能用g++
- 严格来说,这句话不算错误,但是它混淆了概念,应该这样说:编译可以用gcc/g++,而链接可以用g++或者gcc -lstdc++。
- gcc命令不能自动和C++程序使用的库联接,所以通常使用g++来完成联接。但在编译阶段,g++会自动调用gcc,二者等价
05 GCC常用参数选项
| gcc编译选项 | 说明 |
|---|---|
| -E | 预处理指定的源文件,不进行编译 |
| -S | 编译指定的源文件,但是不进行汇编 |
| -c | 编译、汇编指定的源文件,但是不进行链接 |
| -o [file1] [file2] / [file2] -o [file1] | 将文件file2 编译成可执行文件file1 |
| -I directory | 指定include包含文件的搜索目录 |
| -g | 在编译的时候,生成调试信息,该程序可以被调试器调试 |
| -D | 在程序编译的时候,指定一个宏 |
| -w | 不生成任何警告信息 |
| -Wa11 | 生成所有警告信息 |
| -On | n的取值范围:0~3。编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值(默认),-O3优化级别最高 |
| -l | 在程序编译的时候,指定使用的库 |
| -L | 指定编译的时候,搜索的库的路径。 |
| -fPIC/fpic | 生成与位置无关的代码 |
| -shared | 生成共享目标文件,通常用在建立共享库时 |
| -std | 指定C方言,如:-std=c99,gcc默认的方言是GNU C |
1.2 静态库
01 什么是库
- 库文件是计算机上的一类文件,可以简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类。
- 库是特殊的一种程序,编写库的程序和编写一般的程序区别不大,只是库不能单独运行。
- 库文件有两种,静态库和动态库(共享库),区别是:静态库在程序的链接阶段被复制到了程序中;动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。
- 库的好处:1.代码保密 2.方便部署和分发
02 静态库的制作
-
命名规则:
◆ Linux:libxxx.a
lib:前缀(固定)
xxx:库的名字,自己起
.a:后缀(固定)
◆ Windows:libxxx.lib -
静态库的制作:
◆ gcc获得.o文件
◆ 将.o文件打包,使用ar工具(archive)
ar rcs libxxx.a xxx.o xxx.o
r - 将文件插入备存文件中
c - 建立备存文件
s - 索引
示例
在calc目录下有head.h、add.c、main.c三个文件,head.h是头文件引用功能,main.c是测试用,add.c是写了一个加法函数

head.h的内容
#ifndef __HEAD__
#define __HEAD__
int add(int a, int b);
#endif
main.c的内容
#include <stdio.h>
#include "head.h"
int main()
{
int a = 20;
int b = 12;
printf("a = %d, b = %d\n", a, b);
printf("a + b = %d\n", add(a, b));
}
add.c的内容
#include <stdio.h>
#include "head.h"
int add(int a, int b)
{
return a + b;
}
首先生成add.o文件,然后利用ar生成静态库

03 静态库的使用
这是library目录下的树状图,include目录下存放头文件,lib目录下存放库文件,src目录下存放源文件。首先将上面制作的静态库复制到lib目录下

-I指定include包含文件的搜索目录,否则会提示找不到head.h。-l(小写L)指定使用的库,注意这里是库名称,不是文件名。-L指定编译的时候,搜索的库的路径。
编译完成后运行,如下图结果

1.3 动态库
01 动态库的制作
-
命名规则:
◆ Linux:libxxx.so
lib:前缀(固定)
xxx:库的名字,自己起
.so:后缀(固定)
在Linux下是一个可执行文件
◆ Windows:libxxx.dll -
动态库的制作:
◆ gcc得到.o文件,得到和位置无关的代码
gcc -c -fpic/-fPIC a.c b.c
◆ gcc得到动态库
gcc -shared a.o b.o -o libxxx.so
示例
这是提前准备好的calc目录和library目录,文件内容与上面静态库的一样

首先制作动态库,按照上文提到的步骤得到和位置无关的.o文件,然后得到动态库

然后使用动态库编译,编译成功

当运行时报错,显示找不到libcalc.so这个动态库,那么为什么会报错呢?这要从静态库和动态库的工作原理说起

02 静态库和动态库的工作原理
- 静态库:GCC进行链接时,会把静态库中代码打包到可执行程序中
- 动态库:GCC进行链接时,动态库的代码不会被打包到可执行程序中
- 程序启动之后,动态库会被动态加载到内存中,通过ldd(list dynamic dependencies)命令检查动态库依赖关系
- 如何定位共享库文件呢?
当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。此时就需里系统的动态载入器来获取该绝对路径。对于elf格式的可执行程序,是由ld-linux.so来完成的,它先后搜索elf文件的DT_RPATH段 —> 环境变量LD_LIBRARY_PATH —> /etc/ld.so.cache文件列表 —> /lib/,/usr/lib目录找到库文件后将其载入内存。
动态库报错的原因以及解决方案
利用ldd命令查看动态库依赖关系

可以看到其他动态库在=>后面都指有绝对路径或内存地址,而libcalc.so没有。现在我们知道了,报错的原因是只知道动态库的名字,但是不知道它在哪里,也就是它的绝对路径是什么。依照ld-linux.so的搜索顺序,DT_RPATH段是不能够更改的,所以有以下解决方案
配置环境变量
-
临时配置
首先cd到lib目录下用pwd返回动态库的绝对路径,然后运行下面那行命令(LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/linux/libtest/library/lib的解释:用$拿到LD_LIBRARY_PATH本身的内容,再在后面追加上动态库的绝对路径/root/linux/libtest/library/lib,两者中间用':'隔开。可以看到已经可以找到动态库的绝对路径并运行程序了。
![]()
![]()
这种配置方法在关闭xshell连接再次启动后不会保留 -
用户级别配置(永久配置)
在home目录下有一个隐藏文件.bashrc,我们在这个文件中配置即可
![]()
vim进入文件,在文件的最后面插入临时配置时的那一行命令,wq退出
![]()
然后source ./.bashrc使修改生效
![]()
-
系统级别配置(永久配置,需要root权限)
在etc/profile中配置
![]()
在最后一行插入临时配置时的那一行命令,wq退出
![]()
然后source /etc/profile使修改生效
![]()
配置/etc/ld.so.cache文件
/etc/ld.so.cache不能直接修改,我们可以通过修改/etc/ld.so.conf文件间接修改/etc/ld.so.cache文件

在文件最后一行直接插入绝对路径即可

ldconfig命令使修改生效


配置/lib/,/usr/lib目录
可以直接把动态库文件放在/lib/、/usr/lib目录下,但是不推荐这样做,/lib/、/usr/lib目录下有许多系统文件,如果一不小心自定义的动态库文件名与系统文件名重复了就会出现不好的后果。
1.4 静态库与动态库的对比
01 程序编译成可执行程序的过程

02 静态库制作过程

03 动态库制作过程

04 静态库的优缺点
- 优点:
- 静态库被打包到应用程序中加载速度快
- 发布程序无需提供静态库,移植方便
- 缺点:
- 消耗系统资源,浪费内存
- 更新、部署、发布麻烦

05 动态库的优缺点
- 优点:
- 可以实现进程间资源共享 (共享库)
- 更新、部署、发布简单
- 可以控制何时加载动态库
- 缺点:
- 加载速度比静态库慢
- 发布程序时需要提供依赖的动态库









浙公网安备 33010602011771号