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,一经查实,立即删除!

相关文章

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;可以帮助你在家…

[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 进行返回&…

代码算法训练营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开发前的准备 安…

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

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

springboot项目引入swagger

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

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

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

Jmeter —— jmeter利用取样器中http发送请求

使用Jmeter发送HTTP请求 取样器是用来模拟用户操作&#xff0c;向服务器发送请求以及接收服务器的响应数 据的一类元件&#xff0c;其中HTTP请求取样器是用来模拟常用的http请求的 步骤如下&#xff1a; 步骤一&#xff1a;添加线程组 右击测试计划——添加——线程&#x…

如何制作exe文件第一步

目录 0.图片链接1.Welcome&#xff08;可跳过&#xff09;2.Project type--作用选择制作jar包的模式3.定义生成exe应用文件命名和输出地址4.配置执行信息4.1配置应用执行显示方式、安装名称、和显示图标4.2是否重定向日志文件&#xff08;根据需要进行选择&#xff09;4.3配置安…

Docker使用— Docker部署安装Nginx

Nginx简介 Nginx 是一款高性能的 web 服务器、反向代理服务器以及电子邮件&#xff08;IMAP/POP3/SMTP&#xff09;代理服务器&#xff0c;由俄罗斯开发者伊戈尔塞索耶夫&#xff08;Igor Sysoev&#xff09;编写&#xff0c;并在2004年10月4日发布了首个公开版本0.1.0。Nginx…

深入理解Linux veth虚拟网络设备:原理、应用与在容器化架构中的重要性

在Linux网络虚拟化领域&#xff0c;虚拟以太网设备&#xff08;veth&#xff09;扮演着至关重要的角色&#x1f310;。veth是一种特殊类型的网络设备&#xff0c;它在Linux内核中以成对的形式存在&#xff0c;允许两个网络命名空间之间的通信&#x1f517;。这篇文章将从多个维…

40.Python从入门到精通—Python3 JSON 数据解析 Python3 日期和时间 什么是时间元组? 获取当前时间 获取格式化的时间

40.Python从入门到精通—Python3 JSON 数据解析 Python3 日期和时间 什么是时间元组&#xff1f; 获取当前时间 获取格式化的时间 Python3 JSON 数据解析Python3 日期和时间什么是时间元组&#xff1f;获取当前时间获取格式化的时间 Python3 JSON 数据解析 Python3 中可以使用…

SD-WAN企业组网塑造智能网络

云桥通SD-WAN技术正在成为企业网络架构的主流选择&#xff0c;它通过智能管理和控制网络&#xff0c;为客户提供灵活、安全和高效的网络连接&#xff0c;以满足不断增长的业务需求。 云桥通SD-WAN为客户提供的业务能力&#xff1a; A. 提高网络性能 通过智能路由和负载均衡功…