深度解析Linux中的编译器gcc/g++

gc++只用来编译c语言

g++用来编译C/c++

程序的翻译步骤经历四个过程的

1、预处理(进行宏替换/去注释/条件编译/头文件展开等)

深度解析Linux中的编译器gcc/g++

这个-E的意思是从现在开始,进行程序的翻译,一但预处理做完了,就停下来

那么这里的code.i保存的是预处理之后的结果

我们这里的-o选项就是指明了我们的生成文件的名称了

那么我们将这个code.i文件和原始的code.c进行对比下

深度解析Linux中的编译器gcc/g++

对比发现我们的源文件有24行,预处理结算的代码有800多行

我们在这个阶段进行了进行宏替换/去注释/条件编译/头文件展开等等操作

所谓的头文件展开就是将你要包含的头文件的相关内容拷贝到我们的源文件里面

2、编译(生成汇编)

输入命令gcc -S code.i -o code.s

生成了一个code.s的文件

那么这个文件里面的都是汇编语言了

这个-S的意思即是从现在开始进行程序翻译,编译做完,形成汇编,就停下来

3、汇编(生成机器可以识别的代码)

输入命令gcc -c code.s -o code.o

-c的意思是开始进行程序的翻译,汇编完成就停下来

我们生成的code.o文件是不能执行的,并且可以重定位目标二进制文件的

4、链接(生成可执行文件或者库文件)

我们的.o,库文件进行链接,然后就能进行执行的操作

我们可以通过ESc来记住这个前面三个过程的选项

那么分别形成的文件的后缀就是.iso了

如果我们出现这种情况的话,那么就是说明系统觉得我们没有权限

所以我们是需要进行配置文件的更改操作的

深度解析Linux中的编译器gcc/g++

我们需要进入到/etc/sudoers这个文件里面,我们需要使用我们的root账户,不然得话我们进去之后什么都看不见的

我们使用su切换到管理员然后进入到我们的vim模式

对文件进行修改,我们输入99gg直接跳转到第99行就行了

然后我们将root那一行进行复制

然后再粘贴到下面,将我们其他的用户名输入进去我们再输入:wq!就行了

强制保存退出

然后我们的配置文件就修改成功了

那么我们的文件就能进行文件的创建和查看的操作了

为什么我们需要进行汇编呢?

减少语言开发的成本

下面的就是编译器自举的操作

深度解析Linux中的编译器gcc/g++

动静态库和动静态库链接

库:动态库、静态库

linux中的动态库的文件—libXXX.so

在Linux中的静态文件—-libXXX.a

windows中的动态库—XXXX.dill

在Windows中的动态库—XXXX.lib

如果我们使用的是gvv -c code.c的话,我们没有在后面指定上我们的文件名称的话,那么就会生成同名的.o文件

代码语言:JavaScript代码运行次数:0运行复制

[kk@hcss-ecs-28de lesson5]$ gcc -c code.c[kk@hcss-ecs-28de lesson5]$ ll -altotal 16drwxrwxr-x 2 kk kk 4096 Dec 27 14:49 .drwx------ 5 kk kk 4096 Dec 27 14:47 ..-rw-rw-r-- 1 kk kk   69 Dec 27 14:47 code.c-rw-rw-r-- 1 kk kk 1504 Dec 27 14:49 code.o[kk@hcss-ecs-28de lesson5]$ 
深度解析Linux中的编译器gcc/g++

前面的知识:我们的这个code.o是不能进行执行的操作的,我们是需要进行链接的

我们可以再次进行gcc code.o -o mycode的可执行文件

这个操作我们叫做链接了

一点开始链接了,那么就形成了一个mycode的文件

深度解析Linux中的编译器gcc/g++

我们使用ldd mycode来进行查看我们是否存在这个链接成功了

深度解析Linux中的编译器gcc/g++

那么我们根据这个链接找到的就是这个libc- 2.17.so

这个就是我们的C标准库

深度解析Linux中的编译器gcc/g++

gcc会帮你进行c语言的库的链接操作部

代码语言:javascript代码运行次数:0运行复制

[kk@hcss-ecs-28de lesson5]$ file mycode mycode: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for gnu/Linux 3.2.0, BuildID[sha1]=8bbc1c98d1da83282c41f5b2eca3a7a97847630a, not stripped[kk@hcss-ecs-28de lesson5]$ 
深度解析Linux中的编译器gcc/g++

这里我们可以发现我们这里的是动态链接

那么我们如何行成静态链接呢?

我们输入命令gcc code.o -o mycode -Static

那么这个就是采用静态链接的方案

所以gcc默认形成的可执行程序是动态链接的

gcc -static:我们要求程序进行静态链接

我们这里如果要使用动态链接的话就要使用动态库

如果要使用静态链接的话就使用静态库

深度解析Linux中的编译器gcc/g++

需要存在对应的库,我们的Linux默认只存在动态库

sudo yum install glibc-static libstdc++-static -y我们可以使用这个命令进行静态库的安装操作

深度解析Linux中的编译器gcc/g++
深度解析Linux中的编译器gcc/g++

形成的可执行程序里面的static的体积比较大,

动态链接就是将库的地址拷贝到我们的可执行程序里面,动态库也称之为共享库

深度解析Linux中的编译器gcc/g++

动态库不能缺失,不然得话会导致所有的程序运行出错的

静态链接:将我们要的库方法实现,直接拷贝到我们的可执行程序中

所以这就是被为什么我们采用静态链接时候我们的可执行程序的大小比动态链接的时候大了

因为静态链接不再依赖任何库

我们这里的图比较形象

将我们视为可执行程序

学校视作内存

网吧视作动态库

编译器就是大哥

可执行程序和编译器产生链接,告诉我们动态库的地址信息

然后我们在内存中执行程序的时候然后执行到上网这一步了

我们直接就调用动态库了

上完了网吧回学校就是库函数调用完毕了

假如说你的同学都是可执行程序,他们都会进行调用动态库的操作,但是这个时候网吧倒闭了

就是动态库缺失了,那么我们可执行程序的就会出问题了

那么现在我们随身带上电脑,就是我们可执行程序直接将我们的库方法实现,就不用进行库函数的调用了

那么这个就是静态操作

直接将可执行程序和库方法合并起来,内存要大得多

我们可执行程序的时候默认是动态链接的,因为可执行文件的体积比较小

动态链接的优点:生成的可执行文件比较小

动态链接的缺点:一但这个库丢失了,那么我们所有的可执行文件都运行不了了

静态链接的优点:一但编译好不依赖任何库,浪费磁盘和内存空间

在 Linux 系统中,gcc 和 g++ 是 GNU 编译器集合中的核心工具,分别用于编译 C 和 C++ 程序。它们支持生成可执行文件,并且能够链接静态库和动态库。以下是关于它们以及动、静态库的详细解析。


一、GCC/G++ 的基本使用1. 编译阶段

源文件编译为目标文件:

代码语言:javascript代码运行次数:0运行复制

gcc -c file.c -o file.og++ -c file.cpp -o file.o

-c 选项表示只进行编译,不链接。生成的 .o 文件是目标文件。2. 链接阶段

目标文件链接为可执行文件:

代码语言:javascript代码运行次数:0运行复制

gcc file.o -o outputg++ file.o -o output

3. 编译并链接(一步完成)

直接编译源文件并生成可执行文件:

代码语言:javascript代码运行次数:0运行复制

gcc file.c -o outputg++ file.cpp -o output

二、Linux 动态库与静态库1. 静态库

特点:

静态库在编译时被直接嵌入到可执行文件中,生成的二进制文件是独立的。文件后缀通常为 .a。

创建静态库:

代码语言:javascript代码运行次数:0运行复制

gcc -c file1.c file2.c  # 编译生成目标文件ar rcs libmylib.a file1.o file2.o  # 创建静态库

ar 是静态库管理工具。rcs 参数: r:插入文件到库中。c:创建库。s:生成符号索引表。

使用静态库:

代码语言:javascript代码运行次数:0运行复制

gcc main.c -L. -lmylib -o output

-L.:指定库所在路径。-lmylib:链接静态库 libmylib.a(注意去掉前缀 lib 和后缀 .a)。2. 动态库

特点:

动态库在运行时被加载,可执行文件依赖于动态库文件。文件后缀通常为 .so。动态库具有节省存储空间和支持共享的优点。

创建动态库:

代码语言:javascript代码运行次数:0运行复制

gcc -fPIC -c file1.c file2.c  # 编译为位置无关代码gcc -shared -o libmylib.so file1.o file2.o  # 创建动态库

-fPIC:生成位置无关代码(position Independent Code)。-shared:指定生成共享库。

使用动态库:

代码语言:javascript代码运行次数:0运行复制

gcc main.c -L. -lmylib -o outputexport LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH  # 指定动态库路径./output

LD_LIBRARY_PATH 环境变量指定动态库的查找路径。


三、动静态库的选择

对比项

静态库

动态库

文件大小

可执行文件较大,库内容嵌入其中

可执行文件较小,运行时需要库文件支持

运行效率

加载速度更快,无需查找库文件

运行时加载,可能略慢

共享能力

不支持共享,每个程序都包含独立的副本

支持共享,多个程序可以使用同一个库

版本控制

更新库需要重新编译程序

更新库无需重新编译,但需注意兼容性


四、GCC/G++ 动静态库的链接细节1. 链接顺序

编译器按照以下顺序查找库:

指定的路径(-L 参数)。环境变量 LD_LIBRARY_PATH 中指定的路径。系统默认路径 /lib 和 /usr/lib。2. 动态库优先级

如果同一个路径下同时存在静态库和动态库,默认优先链接动态库。

强制使用静态库:

代码语言:javascript代码运行次数:0运行复制

gcc main.c -L. -static -lmylib -o output

-static 选项强制链接静态库。3. 解决动态库版本兼容问题

动态库通常通过符号链接来管理版本:

代码语言:javascript代码运行次数:0运行复制

libmylib.so -> libmylib.so.1.0libmylib.so.1 -> libmylib.so.1.0

可执行文件加载 libmylib.so,符号链接指向具体版本。


五、工具补充

查看目标文件/库文件的符号表:

代码语言:javascript代码运行次数:0运行复制

nm file.onm libmylib.anm libmylib.so

查看动态库依赖:

代码语言:javascript代码运行次数:0运行复制

ldd output

输出依赖的动态库列表及路径。

检查动态库是否导出特定符号:

代码语言:javascript代码运行次数:0运行复制

objdump -T libmylib.so | grep symbol_name

添加动态库路径到系统默认: 将动态库路径添加到 /etc/ld.so.conf,然后运行:

代码语言:javascript代码运行次数:0运行复制

sudo ldconfig

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享