【Linux】Linux编译器 gcc 的使用 | 动静态库的初步认识

news2024/11/19 3:17:37

👑作者主页:@进击的安度因
🏠学习社区:进击的安度因(个人社区)
📖专栏链接:Linux

文章目录

  • 一、前言
  • 二、gcc 演示翻译环境
    • 1、预处理
    • 2、编译
    • 3、汇编
    • 4、链接
    • 5、总结
  • 三、动静态链接库
    • 1、库的认识
    • 2、链接方式
    • 3、动态库与静态库
    • 4、两种链接方式的使用
  • 四、gcc 选项汇总
  • 五、结语

一、前言

在上一篇 Linux 博客中,我们讲解了 vim 编辑器的使用,那么在 Linux 上写代码就没问题。但是写的代码如何编译?这就要用到我们今天讲的内容 —— gcc 编译器。

请添加图片描述

在 Linux 中,C 语言用 gcc 编译;C++ 用 g++ 编译。

我们今天的目标就是学会如何使用 gcc ,了解程序经过翻译环境形成可执行程序的过程,并且讲解动静态链接库的知识。

二、gcc 演示翻译环境

对于一个 C 程序,从源文件到形成可执行程序一共要进行四步:预处理、编译、汇编、链接 。这四步过程被称为 翻译环境

接下来,我们用 gcc 分别演示这四个过程。

1、预处理

在预处理中,需要完成头文件的展开、宏替换、去注释、条件编译等工作。

经过预处理后,当前文件还是 C 语言,只不过变得更加精简。

预处理指令

gcc -E file.c -o file.i
  • -E 选项:让 gcc 进行程序的预处理,预处理之后就停下来。
  • -o 选项:-o 选项紧跟目标生成文件的名称。gcc -o file.i file.c 或者上方的指令都是可以的。

样例程序

#include <stdio.h>

#define FOO // #define 后定义内容可为空
#define MAX 100

int main()
{
    int max = MAX;
    printf("%d\n", MAX);

    printf("hello PRINT %d\n", MAX);
    printf("hello PRINT %d\n", MAX);
    printf("hello PRINT %d\n", MAX);
    printf("hello PRINT %d\n", MAX);
    printf("hello PRINT %d\n", MAX);
    printf("hello PRINT %d\n", MAX);

    #ifdef FOO 
    printf("yes");
    #else
    printf("no");
    #endif

    return 0;
}

进行预处理

image-20230108192059994

预处理之后生成 file.i 文件,分别打开 .c 和 .i 文件进行对比 :

image-20230108192229276

我们发现代码变为八百多行,这是因为头文件 #include <stdio.h> 被展开,同时预处理的其他过程也都进行了。

2、编译

当程序在编译时,gcc 会检查代码是否有语法错误,了解代码基本内容,检查无误就会把代码翻译为汇编语言。

汇编语言中包含源文件的部分内容,所以也可以说该文件为 C 语言汇编代码

编译指令

gcc -S file.i -o file.s
  • -S 选项:让 gcc 进行程序的编译,编译之后就停下来。
  • -o 选项的位置可以改变,但是一定要记住 -o 选项的规则。
  • .s :生成汇编代码的后缀

通过源文件 .c 也可以形成编译后的文件,指令为 gcc -s file.c -o file.s,但是我们为了演示过程的连续性,我们统一从上次生成的文件开始执行指令。

进行编译

image-20230108193743887

观察 file.s 文件内容:

image-20230108193936227

上部分框起来的语句含有 c 语言的样子;而下部分框起来的语句则是汇编代码,由此证明在编译过后生成的文件为 C 语言汇编代码。

3、汇编

汇编之后会把汇编语言转换为二进制文件。

这里的二进制文件可以说是 可重定位二进制文件 。该文件不能被执行,类似于 windows 上的 .obj 文件。

这一步知识把我们自己的代码翻译为二进制目标文件。

由于机器只认识二进制文件,所以换言之,这一步就是把人能看懂的文件,翻译成机器能看懂的,就是 生成机器可识别代码

汇编指令

gcc -c file.s -o test.o
  • -c 选项:让 gcc 进行程序的汇编,汇编之后就停下来。
  • .o :生成二进制文件的后缀。

进行汇编

image-20230108194713677

观察 file.o 内容:

image-20230108194740871

打开文件发现什么都看不懂。

这就是二进制文件,计算机可以识别该文件。

那么该文件可以执行吗?

image-20230108195435564

不可以执行,报错信息为权限被拒绝,通常为权限不够。那这怎么证明该文件不能执行?

我们将其提权试试:

image-20230108195625787

然后再次 ./file.o 执行该文件:

image-20230108195707109

仍然执行失败,并报错:无法执行该二进制文件。所以这就证明我们的结论:汇编生成的二进制文件不可执行!

4、链接

链接过程就是将程序和对应的库链接起来,编译器会自动识别语言。

链接指令

gcc file.o -o myfile
  • 生成文件为可执行程序
  • gcc file.o 也可以完成链接

进行链接

image-20230108200405269

再执行生成的可执行程序 file

image-20230108200508234

5、总结

我们这边将翻译过程按步执行只是为了让小伙伴们理解程序由源代码到可执行程序的一个过程。

实际上,在我们日常编译代码只需要 gcc file.c 就可以一步生成可执行文件。

在提供一个记忆方式:

对于 预处理、编译、汇编 三步的选项分别为 ESc ,可以与键盘上的 esc 键来帮助记忆,区别前两个字母是大写。而生成的文件后缀分别为 iso 可以利用国际标准化组织(ISO)来记忆。

三、动静态链接库

1、库的认识

首先清楚一点,我们写的代码里面经常会用库函数,这些调用接口的方式是我们写的,但是这些库函数的底层实现不是我们写的,而这中间就有一个调用库的过程。

在链接的过程中,需要对形成的二进制文件和库文件进行合并从而形成可执行程序,这边就调用了库。库是能成功编译的必要条件

我们一直在使用库,至少是语言上的库。

比如上方链接生成的可执行程序 file ,它就依赖了库。

通过 ldd file ,就可以查看该程序依赖了哪些库:

image-20230108202402892

不仅程序依赖库,我们在 linux 下能进行代码编写也是因为内置了库。linux 默认有语言级别的头文件,和语言对应的库。

通过 ls /usr/include/ 就可以看到系统的内置的头文件,我们通过头文件就可以根据库函数,找到对应的方法,从而链接到正确的库:

image-20230108202720173

在linux 下,库分两种

  1. 静态库,格式为 libXXX.a
  2. 动态库,格式为 libXXX.so

基于上面的格式,不同的库还可能会有版本,例如 file 程序就依赖了libc.so.6 ,后面的 .6 就是版本:

image-20230108210539899

并且我们发现可执行程序依赖的就是 .so 动态库。

对于动静态库,以lib 为前缀,.a/.so 为后缀,掐头去尾就是名称。例如 libc.so.6 ,它的名字就是 c ,是 c 语言内置库。

而以上动静态库的后缀为 linux 上特有的后缀划分;如果是 windows 下静态库为 lib ,动态库为 dll 。

不止可执行程序,就连指令也依赖于库,我们观察几个指令

image-20230108204030245

常用的 ls, tar 它们都依赖于库,甚至还依赖于 c 库。

由此发现,指令和 c 程序一样,都依赖与库。所以我们可以将指令看待为:指令是程序,是工具,也是“指令(字面意思)”

虽然从编译角度来说,指令和我们写的 c 程序没有任何区别,但是从功能上来说确是天差地别。

讲了这么多,实际上也就是一句话,库是必要的,并且库分为动态库和静态库

2、链接方式

Linux 的链接方式就两种:

  • 动态链接
  • 静态链接

而 Linux 默认的链接方式为 动态链接 ,我们来证明一下:

file 指令查看 file 文件的类型:

image-20230108212654870

我们发现生成的可执行程序默认就是采用的 动态链接

那么为什么采用静态链接而不采用动态链接?这里面有什么原因?

我们先了解一下动静态库。

3、动态库与静态库

动态库对应的链接方式为动态链接;静态库对应的链接方式为静态链接。

动态链接必须使用动态库,静态链接必须使用静态库。

动态库优缺点

动态库也可以说是共享库。动态库在链接的时候,并没有把相应的库文件加载到可执行程序中,而是在运行的时候,拷贝库中所需代码的地址到可执行程序中相关的位置。

通过这种链接方式,使用动态库就大大节省了内存损耗。

但是这也带来一个缺点,由于动态库链接时,是通过地址链接的。所以只要我们的动态库缺失,程序便无法运行。

静态库优缺点

静态库则是在编译链接时,把库文件的代码全部加载到可执行程序中。

这种链接方式导致生成文件体积庞大。若文件数目一多,到时候内存就会被占用很多。

但是这也有一个优点,这样就使得程序不依赖于库了。即使库出了问题,也没事,因为我们已经将代码加载到程序中了。

通过上面,我们发现,动态库可以大大节省内存开销,并且一般对应的动态库并不会出问题,再衡量可执行程序很多的情况,动态链接其实是最好的链接方式。

所以 Linux 默认是采用动态链接的链接方式。

4、两种链接方式的使用

对于动态链接,我们已经使用过了,比如上文生成的 file 文件,就是动态链接生成的。

而静态链接则需要通过特定方式来使用,并且 Linux 默认只装了动态库,静态库是没装的,所以先安装一下:

sudo yum install -y glibc-static

安装完毕后,就可以使用了。

静态链接的使用方式为:

gcc -o file.c -o file-static -static
  • -o 后面还是自定义名称,这里只是为了做区别
  • 结尾的 -static 代表以静态链接方式编译

image-20230108215915474

我们发现,静态链接后生成的可执行程序的体积非常大。

再用 file file-static 查看一下文件类型:

image-20230108220040753

就变成 statically linked:静态链接了。

四、gcc 选项汇总

gcc 常用的选项和操作也就是 -o 选项还有 gcc file 直接生成可执行程序。

下面对其他选项做出一些补充:

  • -E :只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
  • -S :编译到汇编语言不进行汇编和链接
  • -c :编译到目标代码
  • -o :文件输出到文件
  • -static :此选项对生成的文件采用静态链接
  • -g :生成调试信息。GNU 调试器可利用该信息
  • -shared :此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库
  • -O0 :不做任何优化,这是默认的编译选项。
  • -O1 : 对程序做部分编译优化
  • -O2 :是比O1更高级的选项,进行更多的优化
  • -O3 :比O2更进一步的进行优化
  • -w :不生成任何警告信息
  • -Wall: 生成所有警告信息

五、结语

到这里,本篇文章就到此结束了。

gcc 的选项很多,但是我们实际上用的多的也就只有 -o 选项而已。且编译程序也可以 gcc file 一步到位。但是对于程序编译的过程用到的选项,还有这些步骤我们还是有必要了解的。

而今天的重点 a n d u i n anduin anduin 认为是动静态库的认识。实际上动静态库还有很多知识,这些得到以后的基础 I/O 讲,这边我们就是初步认识。本篇博客只要搞清楚两种动静态库和两种链接方式即可。

如果觉得 a n d u i n anduin anduin 写的不错的话,可以三连支持一下哦!我们下期见~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/149161.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录算法训练营第6天 1.两数之和、242. 有效的字母异位词、349.两个数组的交集

代码随想录算法训练营第6天 1.两数之和、242. 有效的字母异位词、349.两个数组的交集 两数之和 力扣题目链接(opens new window) 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。 首…

JS数字日期转中文日期(封装函数,dayjs转换时间格式)

JS数字日期转中文日期往期相关文章场景复现封装函数&#xff08;数字日期转中文日期&#xff09;实际应用往期相关文章 文章内容文章链接JS数组对象——根据日期进行排序&#xff0c;按照时间进行升序或降序排序https://blog.csdn.net/XSL_HR/article/details/128579840?spm1…

Markdown使用说明

Markdown使用说明欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注…

[网鼎杯 2020 朱雀组]phpweb

目录 信息收集 方法一&#xff1a;in_array()函数绕过 方法二&#xff1a;反序列化漏洞利用 信息收集 抓个包&#xff0c;发现POST传入以下内容 funcdate&pY-m-dh%3Ai%3Asa func和p的值分别为一个待执行的函数和函数的参数 构造payload 尝试funcphpinfo&p 回显 H…

【学习笔记之Linux】权限

权限概念 一件事是否允许被谁“做”&#xff0c;这就是权限。权限 用户 文件属性。   在Linux上&#xff0c;用户分为普通用户和root。root是超级管理员 ≈ 天王老子&#xff0c;只能够有一个。root的命令提示符是#&#xff1b;普通用户通过root创建&#xff0c;可以有多个…

【案例教程】地下水环评(一级)实践技术及Modflow地下水数值模拟

【前沿】地下水数值模拟技术应用与地下水环评报告编制方法实践线上直播课程&#xff0c;主要围绕的环评导则&#xff0c;结合不同行业类别&#xff0c;实例讲解地下水环境影响评价的原则、内容、工作程序、方法。包括数据处理分析、数值模型构建以及环评报告编写等。涉及地下水…

【自学C++】C++ int

C int C int教程 C 中的 int 用来表示一个 整数&#xff0c;也可以叫做整型&#xff0c;int 的取值范围是介于 short 和 long 之间的。 C int定义详解 语法 int varname value;参数 参数描述int定义 int 类型变量使用的类型。varname变量名。value可选&#xff0c;变量的…

Linux应用编程---9.消息队列

Linux应用编程—9.消息队列 ​ 消息队列用于进程之间的通讯&#xff0c;可以在如父子进程、兄弟进程这样的具有亲缘关系的进程之间传递数据&#xff0c;也可以用于具有非亲缘关系的进程之间通讯。消息队列可以传递结构体&#xff0c;所以可以发送任意数据类型。与消息队列有关…

数据结构(一)——链表

链表与邻接表 介绍 链表作为一种基础数据结构&#xff0c;具有几个特点&#xff1a; 优点&#xff1a;插入、删除非常快&#xff08;需要知道需要插入和删除节点前一个位置&#xff09;缺点&#xff1a;查询、访问&#xff08;用索引&#xff09;非常的慢 链表的创建方法一…

Selenium用法详解【cookies操作】【JAVA爬虫】

简介本文主要讲解java代码利用Selenium控制浏览器获取网站的cookies,对网站cookies的相关操作教程。cookies操作cookies 是识别用户登录与否的关键&#xff0c;爬虫中常常使用 selenium jsoup 实现 cookie持久化&#xff0c;即先用 selenium 模拟登陆获取 cookie &#xff0c;…

你可能从未想过的:人工智能未来50年的安全领域问题

前言 随着人工智能技术的普及和发展&#xff0c;很多人工智能出现的故障和问题也会愈发明显。本文简单讲述了未来50年人工智能发展过程中可能会出现的景象和问题。 一、人工智能独立 尽管很可能第一批人工智能是由人类发明制作的&#xff0c;但随着大量基础设施的完善&#x…

javaweb-会话技术CookieSession

文章目录会话技术Cookie&Session1&#xff0c;会话跟踪技术的概述2&#xff0c;Cookie2.1 Cookie的基本使用2.2 Cookie的原理分析2.3 Cookie的使用细节2.3.1 Cookie的存活时间2.3.2 Cookie存储中文3&#xff0c;Session3.1 Session的基本使用3.2 Session的原理分析3.3 Sess…

4.8、网际控制报文协议 ICMP

为了更有效地转发 IP 数据报和提高交付成功的机会 在网际层使用了网际控制报文协议 ICMP(Internet Control Message Protocol)。 主机或路由器使用 ICMP 来发送 差错报告报文\color{red}差错报告报文差错报告报文和询问报文\color{red}询问报文询问报文。 ICMP报文被封装在IP…

基于采样的规划算法之动态窗口法(DWA)

动态规划将一个多步决策问题拆分成若干子问题,并且保证子问题的最优解能推出完整问题的最优解。所以,动态规划可以得到采样空间下的最优路径解。本章介绍的动态窗口法(Dynamic Window Approach, DWA)与动态规划类似,也是将从起点到终点的多步决策问题拆分成一系列子问题—…

Linux应用编程---1.线程与进程

Linux应用编程—1.线程与进程 1 重要概念 1.1 程序 ​ 程序指的是还没有运行起来的源代码&#xff0c;比如电脑上安装的“Keil MDK”、“Xshell”等等。 1.2 进程 ​ Win10环境下&#xff0c;打开任务管理器&#xff0c;能看到有“进程”一栏&#xff0c;点击进去能看到5个…

2023 Real World CTF体验赛部分Writeup

web1 Thinkphp lang多语言 RCE漏洞&#xff0c;直接打 GET /index.php?config-create/<?eval($_REQUEST[1]);?>/tmp/keep.php HTTP/1.1 Host: 47.98.124.175:8080 Cache-Control: max-age0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; …

QT-5.12: QSqlDatabase: QMYSQL driver not loaded 错误及解决方案

错误现象及原因&#xff1a; &#xff08;1&#xff09;qt SQL模块已默认不编译mysql驱动&#xff0c;导致mysql驱动缺失&#xff0c;错误提示如下&#xff08;可用数数据库驱动不含mysql驱动&#xff0c;执行方法一和二&#xff09;&#xff1b; &#xff08;2&#xff09;已…

C++新基础类型long long, char8_t, char16_t, char32_t

C新基础类型long long, char8_t, char16_t, char32_t一、整数类型 long long二、新字符类型 char16_t 和 char32_t三、char8_t 字符类型一、整数类型 long long 我们知道long通常表示一个32位整型&#xff0c;而long long则是用来表示一个64位的整型。 C标准中定义&#xff0…

vue3包依赖关系

包reactivity &#xff1a;响应式API&#xff0c;例如toRef、reactive、Effect、computed、watch等&#xff0c;可作为与框架无关的包&#xff0c;独立构建runtime-core&#xff1a;平台无关的运行时核心代码。包括虚拟dom渲染、组件实现和JavaScript API。可以使用这个包针对特…

机器学习实战教程(十一):线性回归基础篇

一、前言前面的文章介绍了很多分类算法&#xff0c;分类的目标变量是标称型数据&#xff0c;而本文将会对连续型的数据做出预测。主要讲解简单的线性回归和局部加权线性回归&#xff0c;并通过预测鲍鱼年龄的实例进行实战演练。二、什么是回归&#xff1f;回归的目的是预测数值…