Linux:gcc

Linux:gcc

    • gcc概述
    • 语言发展史
    • gcc的编译过程
      • 预处理
      • 编译
      • 汇编
    • gcc的链接过程
      • 动态库与静态库


gcc概述

GCC(英文全拼:GNU Compiler Collection)是 GNU 工具链的主要组成部分,是一套以 GPL 和 LGPL 许可证发布的程序语言编译器自由软件,由 Richard Stallman 于 1985 年开始开发。

gcc是GCC中的C语言编译器,而g++是GCC中的C++编译器。本博客只讲解gcc,g++的语法和选项和gcc都是一致的。

gcc编译C语言最基本的语法:

gcc test.c

你就完成了对test.c文件的编译操作,该过程会默认生成一个名为a.out的可执行文件,你可以直接运行a.out

如果你想要编译的同时,指定编译后生成文件的名称,就加上-o选项,后紧跟着编译后文件的名称:

gcc test.c -o test.out

此时生成的可执行文件就叫做test.out了。

gcc默认不支持C99标准,如果你希望以C99标准编译该文件,加上选项-std=c99

gcc test.c -o test.out -std=c99

编译C语言要经过预处理编译汇编链接的过程,可是为什么我们需要这些过程呢?这涉及到计算机语言的发展史了。


语言发展史

在最早的时候,对计算机编程是通过打孔纸带的,如果有孔就是1,没孔就是0。但是这种二进制编程的效率太低了,于是计算机的从业者就开始对二进制进行改进,诞生了汇编语言。汇编语言的诞生具有划时代的意义,于是就发展出了操作系统,编译器等东西。比如Unix的最初版,就是通过汇编语言写的。后来为了方便,又诞生了面向过程语言,其中最大名鼎鼎的毫无疑问是C语言。再后来人们觉得面向对象思想是符合人类的编程习惯的,就又有了C++,python,Java这样的高级语言。

现在问题就来了,不论是汇编语言,C语言,还是如今的高级语言。是先有语言,还是先有编译器?这就涉及到编译器自举过程。

编译器自举

在出现汇编语言之前,我们只有二进制语言来编程。但是人们发明汇编语言后,就要有对应的编译器来编译汇编语言,不然计算机就无法理解汇编语言。因为汇编语言无法被编译,所以我们就要先用二进制语言写一个汇编语言的编译器,然后我们的汇编语言就可以被计算机理解了。但是一旦我们的汇编语言可以被解释了,可以用于编程了,那么我就可以再用汇编语言写一个编译器了。从此以后,编译器就用汇编语言再来书写了。这个过程就叫做编译器自举

同理,在C语言出来之前,我们无法用C语言写一个C语言的编译器,所以就用汇编语言写一个C语言的编译器。现在C语言就可以被计算机编译了,于是我们就可以再用C语言写一个C语言的编译器。

那么现在又有一个问题了,请问我们在解释C语言的时候,是直接把C语言解释为二进制指令,让计算机理解。还是先把C语言解释为汇编语言,然后再让汇编语言解释为二进制呢?不得不说,如果直接让C语言变成二进制语言,这个过程未免太麻烦了,二进制语言过于反人类了,相应的汇编语言就好很多。我们要在巨人的肩膀上看世界,汇编语言已经把转换为二进制这个工作做完了,那么我们只需要在汇编语言的基础上改进就好。因此C语言要先变成汇编语言,最后变成二进制。这就是为什么C语言要有预处理编译汇编的过程。


gcc的编译过程

现在我们再简单回顾一下以上三个阶段的功能,并且讲解gcc的编译相关选项。

test.c中,有如下代码:

在这里插入图片描述


预处理

  1. 头文件展开
  2. 去注释
  3. 宏替换
  4. 条件编译

那么经过这个过程,还是C语言吗?答案是是的,该过程只是预先处理了一下C语言,把一些没必要的内容删除,减少后续工作的工作量。处理后依然是C语言代码。

使用gcc时,带上-E选项,就可以得到预处理后的文件:

gcc -E test.c -o test.i

此处我把编译后的文件命名为test.i,一般而言经过预处理的文件都以.i为后缀

左侧是预处理后的文件test.i,右侧是源文件test.c

在这里插入图片描述

  1. 可以看到,左侧代码经过了800多行才到main函数,这800行就是<stdio.h>头文件的展开
  2. 语句#define M 100没有了,并且main函数中的M被替换为了100,也就是完成了宏替换
  3. 被注释的四行//printf("hello world!\n");被删除了,也就是注释的删除

现在再看看条件编译,我把代码改为以下代码:

在这里插入图片描述

如果我们把VERSION2注释掉,那么就会输出"hello VERSION1.0";如果把VERSION1注释掉,那么就会输出"hello VERSION2.0";如果都注释掉,那么就输出"hello free"。我们确实可以在编写代码的时候,通过注释来进行条件编译,控制代码的版本。但是这个过程在编译的时候就可以直接控制。gcc可以在命令行中定义宏

语法:

gcc test.c -o test1.exe -D VERSION1=1

以上命令中,-D VERSION=1就相当于在代码中写了一句#define VERSION1 1-D选项用于指定一个宏。

看到以下现象:
在这里插入图片描述

第一次-D VERSION=1,最后一行输出的hello VERSION1.0;第二次-D VERSION=2,最后一行输出hello VERSION2.0;第三次没有加任何宏定义,就输出了hello free

也就是说我们可以通过指令给编译器传递不同的宏,来动态裁剪代码

比如说visual studio有社区版和专业版,社区版是免费的,专业版要收费,毫无疑问社区版是专业版经过一定阉割后产生的。那么在微软公司内部,这两个版本要分开维护吗?这样的话如果一个版本出现了问题,那还要去看看另外一个版本有没有相同的问题,然后修改两份代码,未免太麻烦了。因此完全可以采用条件编译,把需要被阉割掉的部分用条件编译包起来。最后在编译的时候,使用命令行传入不同的宏,实现一份代码两个版本因此条件编译的最重要的应用场景就是一份代码发行多个版本的软件


编译

该过程是把C语言的代码变成汇编语言的过程。

想要生成该阶段的文件,需要通过-S选项:

gcc -S test.c -o test.s

该阶段的文件,一般以.s结尾。

test.s内部:

在这里插入图片描述

可以看到,内部确实已经变成汇编语言的文件了。


汇编

该过程是把汇编语言变成二进制的过程,这个过程生成的文件叫做目标文件,全称为可重定位目标二进制文件。这个文件虽然已经是二进制文件了,但是它还不是一个可执行文件

想要获得该阶段的文件,需要使用选项-c

gcc -c test.c -o test.o

一般而言,该阶段的文件以.o或者.obj结尾。在Linux中习惯用.o,在Windows中习惯用.obj.

test.o内部:

在这里插入图片描述

可以看到,其内部都是一些乱码,因为我是用的vim是一款文本编辑器,其不能解析二进制文本,所以会出现乱码。

为什么该过程已经是一个二进制文件了,还是不能被计算机执行?计算机不是可以识别二进制吗?

这是因为缺少链接的过程,接下来我们看看链接都有哪些功能。


gcc的链接过程

我们的C语言内部,调用很多库函数,比如printfscanf等等。它们并没有在编译的时候展开,不信你可以回去看看那个.i文件,绝对没有展开一个叫做printf的函数。那么C语言要如何拿到这个函数,并调用它呢?这就涉及到链接的过程。

如果你想要让你的.c.i.s.o中的任意一个文件变成链接后的文件,不用带任何选项,直接执行gcc即可,因为直接执行就是生成可执行文件,这已经是链接后的文件了。

gcc -o test.exe test.c

这样最后生成的test.exe就是可执行文件了,在此.exe不是强制的,只是我前面已经写过太多test命名的文件了,使用后缀区分一下属性。Windows中,可执行文件的后缀就是.exe,因为Windows通过后缀区分文件,而Linux不通过后缀区分文件,因此这个.exe可有可无,只用于帮助我们自己快速判断文件属性

那么这个可执行文件test.exe是如何链接到库的,又链接了那些库呢?

我们可以通过ldd指令来查看一个可执行文件链接了那些库:

ldd test.exe

输出结果:

在这里插入图片描述

比如第二行的libc就是C语言的标准库。另外的,它还指明了一些库在系统中的路径。也就是说我们的很多头文件,都已经早早地在Linux中下载好了,因此我们可以在Linux上运行C语言代码。

比如说/usr/include/路径下的文件:

在这里插入图片描述

你可以看到很多非常非常熟悉的头文件,比如<stdio.h><math.h><stdlib.h>等等。

在链接到库时,库分为两种:动态库静态库


动态库与静态库

  • 静态库:编译链接时,把库文件的代码全部拷贝到可执行文件中,在运行时也就不再需要库文件了。

优点:只要形成了可执行文件,那么就脱离对库的依赖,可以自主运行,可移植性好
缺点:相同的资源拷贝多份,浪费资源,生成的文件比较大

  • 动态库:在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,编译器会提供动态库的地址,当程序执行到指定的代码段,就会去动态库内部查找对应的内容。

优点:节省资源,整个操作系统所有使用动态库的程序,只需要一份库文件,内存中也只加载一份
缺点:一旦丢失,所有链接该库的程序都无法执行了

通过动态库实现的链接,叫做动态链接,通过静态库实现的链接叫做静态链接。这里有一个注意点,那就是:虽然动态库和静态库内部的函数是一样的,但是动态库和静态库是两份不同的文件,并不是说把动态库拷贝一份到代码中,就是静态链接了。这涉及到一些更深的知识,就不在gcc的章节中介绍了。

动静态库的后缀

Linux中,动态库以.so为后缀,静态库以.a为后缀
Windows中,动态库以.dll为后缀,静态库以.lib为后缀

我们直接以一般的形式编译一个文件:

gcc -o test1.exe test.c

就可以得到一个名为test1.exe的文件。先通过ldd查看相关的库:

在这里插入图片描述

可以看到,每个库中都包含了.so的字段,说明gcc在编译时默认使用动态库

我们也可以通过file指令查看,执行file test1.exe

在这里插入图片描述

dynamically linked字段就表示这是一个动态链接的程序。

如果我们想要生成静态链接的文件,则额外加上选项-static

[hxy@iZ2zehtehrgzt3wqccrpyfZ gcc]$ gcc -o test2.exe test.c -static

此时test2.exe文件就是一个静态链接的文件了。

但是你大概率无法执行该命令,会出现以下报错:

在这里插入图片描述

这是因为Linux默认是不带静态库的,要手动安装:

yum install -y glibc-static libstdc++-static

该指令需要root权限,要么sudo,要么以root身份执行。其中glibc-static是C语言静态库,libstdc++-static是C++静态库。

先对比一下两个文件的大小:

在这里插入图片描述

可以看到,两个文件之间差不多相差了100倍的大小,因此静态库非常浪费资源。

使用ldd

在这里插入图片描述

其很明确的告诉你,这不是一个动态链接的可执行文件not a dynamic executeable

再用file

在这里插入图片描述

statically linked字段说明这是一个静态链接的可执行文件。


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

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

相关文章

汽车变速器原理?

汽车的变速器是负责调整发动机输出转速与车辆行驶速度匹配的关键部件。它的基本原理涉及到两个主要部分&#xff1a;齿轮组和离合器&#xff08;对于手动变速器&#xff09;或液力变矩器&#xff08;对于自动变速器&#xff09;。 齿轮组&#xff08;Gear System&#xff09;&a…

SpringBoot整合RabbitMQ-应答模式

一、应答模式 RabbitMQ 中的消息应答模式主要包括两种&#xff1a;自动应答&#xff08;Automatic Acknowledgement&#xff09;和手动应答&#xff08;Manual Acknowledgement&#xff09;。&#xff08;一般交换机发送消息&#xff0c;RabbitMQ只有在接收到消费者的确认后才…

LeetCode-139. 单词拆分【字典树 记忆化搜索 数组 哈希表 字符串 动态规划】

LeetCode-139. 单词拆分【字典树 记忆化搜索 数组 哈希表 字符串 动态规划】 题目描述&#xff1a;解题思路一&#xff1a;Python动态规划五部曲&#xff1a;定推初遍举【先遍历背包 后遍历物品】必须是排列解题思路二&#xff1a;Python动态规划版本二解题思路三&#xff1a;回…

C++——优先级队列

前言&#xff1a;这篇文章我们继续来分享一个c的容器——优先级队列。 一.理解优先级 何为优先级一说&#xff1f;实际上就是有顺序的意思。 优先级队列&#xff0c;即有顺序的队列&#xff0c;是一个无需我们自己进行排序操作&#xff0c;在数据传入时就会由容器自己排好序的…

冒泡排序解读

在信息爆炸的时代&#xff0c;数据无处不在&#xff0c;而如何有效地管理和处理这些数据&#xff0c;成为了现代计算机科学的一个重要课题。排序算法&#xff0c;作为数据处理的基本工具之一&#xff0c;对于数据的组织、搜索和分析起着至关重要的作用。今天&#xff0c;我们就…

在家学机器人技术指南

机器人技术是一个跨学科的领域&#xff0c;涉及计算机科学、电子工程、机械工程、人工智能等多个方面。在家自学机器人技术是完全可能的&#xff0c;但需要有计划和系统的学习路径&#xff0c;以及对相关领域的基础知识有一定的了解。 以下是一些建议&#xff0c;可以帮助你在家…

Jeecg的Dict注解的用法

Jeecg 是一个基于代码生成器的快速开发框架&#xff0c;它提供了一系列的注解来简化开发过程。其中&#xff0c;Dict 注解是用于将数据库中的数据字典值转换成具体含义的注解。 通常情况下&#xff0c;在数据库中&#xff0c;一些字段的值可能是数字或者代码&#xff0c;而不是…

[C++][算法基础]合并集合(并查集)

一共有 n 个数&#xff0c;编号是 1∼n&#xff0c;最开始每个数各自在一个集合中。 现在要进行 m 个操作&#xff0c;操作共有两种&#xff1a; M a b&#xff0c;将编号为 a 和 b 的两个数所在的集合合并&#xff0c;如果两个数已经在同一个集合中&#xff0c;则忽略这个操…

力扣刷题Days33-209. 长度最小的子数组(js)

目录 1&#xff0c;题目-滑动窗口 2&#xff0c;代码 滑动窗口 3&#xff0c;学习与总结 1&#xff0c;题目-滑动窗口 给定一个含有 n 个正整数的数组和一个正整数 target 。找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1,…

动态路由-基于vue-admin-template

基于 vue-admin-template的动态路由 1. 拆分静态路由与动态路由 静态路由----所有人都可以访问—首页/登录/404 动态路由–有权限的人才可以访问—组织/角色/员工/权限 2. 根据用户权限添加动态路由 获取对应的权限标识(vuex中actions中把用户资料通过return 进行返回&…

AI创业项目:AI旅游规划定制师

在当前的旅游市场中&#xff0c;个性化旅游规划成为越来越多旅行者的需求。然而&#xff0c;现行的定制旅行服务主要依赖于人工定制师&#xff0c;这一模式面临着信息不透明、价格弹性大等挑战。定制师在客户与服务供应商之间掌握着信息差&#xff0c;依靠这一优势获得收益&…

代码算法训练营day14 | 理论基础、递归遍历

day14&#xff1a; 理论基础二叉树的分类&#xff1a;二叉树的种类&#xff1a;满二叉树完全二叉树二叉搜索树平衡二叉搜索树 二叉树的存储方式&#xff1a;链式存储顺序存储 二叉树的遍历方式&#xff1a;深度优先和广度优先遍历实现方式 二叉树的定义&#xff1a; 递归遍历递…

(学习日记)2024.04.11:UCOSIII第三十九节:软件定时器

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

Vue文档

Vue是什么&#xff1f;为什么要学习他 Vue是什么&#xff1f; Vue是前端优秀框架&#xff0c; 是一套用于构建用户界面的渐进式框架 为什么要学习Vue Vue是目前前端最火的框架之一Vue是目前企业技术栈中要求的知识点Vue可以提升开发体验Vue学习难度较低… Vue开发前的准备 安…

JavaScript PAT乙级题解 1057 数零壹

给定一串长度不超过 105 的字符串&#xff0c;本题要求你将其中所有英文字母的序号&#xff08;字母 a-z 对应序号 1-26&#xff0c;不分大小写&#xff09;相加&#xff0c;得到整数 N&#xff0c;然后再分析一下 N 的二进制表示中有多少 0、多少 1。例如给定字符串 PAT (Basi…

Redis数据倾斜

Redis 数据倾斜问题通常出现在分布式 Redis 环境中&#xff0c;尤其是 Redis 集群环境。这意味着一部分节点承载了比其他节点更多的数据或者接收的请求更多&#xff0c;导致负载不均衡。数据倾斜可能会对性能和可扩展性造成影响。以下是一些解决数据倾斜的通用策略&#xff1a;…

分享 3 个实时人工智能图像生成工具

如果有人还需要开源人工智能技术快速发展的实例&#xff0c;那就是实时 Diffusion 。一年前&#xff0c;如果想分析单个单词对图像提示的影响&#xff0c;甚至尝试使用 Diffusion 模型替换视频中的面孔&#xff0c;需要两件事&#xff1a; 处理开源代码自建 WEB 应用程序 到 …

cexprtk:Python中的数学表达式解析和计算

1 安装 可以使用 pip 安装&#xff1a; pip install cexprtk 注意&#xff1a;安装需要安装兼容的 C 编译器。 2 用法 1&#xff09;示例&#xff1a;计算一个简单的方程 计算算术表达式 (55) * 23 import cexprtk cexprtk.evaluate_expression("(55) * 23", {}…

springboot项目引入swagger

1.引入依赖 创建项目后&#xff0c;在 pom.xml 文件中引入 Swagger3 的相关依赖。回忆一下&#xff0c;我们集成 Swagger2 时&#xff0c;引入的依赖如下&#xff1a; <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2&…

2024智能计算、大数据应用与信息科学国际会议(ICBDAIS2024)

2024智能计算、大数据应用与信息科学国际会议(ICBDAIS2024) 会议简介 智能计算、大数据应用与信息科学之间存在相互依存、相互促进的关系。智能计算和大数据应用的发展离不开信息科学的支持和推动&#xff0c;而信息科学的发展又需要智能计算和大数据应用的不断拓展和应用。智…