Linux:gcc

news2024/11/27 17:51:02

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.coloradmin.cn/o/1582117.html

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

相关文章

python--正则表达式,元字符,反义符,转义符,位数问题

正则表达式&#xff08;regular expression&#xff09;&#xff1a; 为什么使用正则表达式&#xff1a; 在软件开发过程中&#xff0c;经常会涉及到大量的关键字等各种字符串的操作&#xff0c;使用正则表达式能很大程度的简化开发的复杂度和开发的效率&#xff0c;所以pytho…

麒麟V10安装Redis6.2.6

1、下载redis安装包 Redis各版本下载&#xff1a;https://download.redis.io/releases/ 2、将下载后的.tar.gz压缩包上传到到服务器自定义文件夹下 3、 解压文件 tar -zxvf redis-6.2.6.tar.gzmv redis-6.2.6 redis4、安装redis 在redis文件夹下输入make指令 cd /opt/redi…

【React】react 初学增删改查购物车案例

界面 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>react-购物车案例</title><…

华为OD机试 - 机器人搬砖 - 二分查找(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

centos 7.9 nginx本地化安装,把镜像改成阿里云

1.把centos7.9系统切换到阿里云的镜像源 1.1.先备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup1.2.下载新的CentOS-Base.repo配置文件 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo特别…

css面试题--定位与浮动

1、为什么需要清除浮动&#xff1f; 在非IE浏览器下&#xff0c;容器不设高度且子元素浮动时&#xff0c;容器高度不能被内容撑开&#xff0c;内容会溢出到容器外面而影响布局。这种现象被称为浮动。 浮动的原理&#xff1a;浮动元素脱离文档流&#xff0c;不占用空间&#xff…

39-性能分析(下):APIServer性能测试和调优实战

在API上线之前&#xff0c;我们需要知道API的性能&#xff0c;以便知道API服务器所能承载的最大请求量、性能瓶颈&#xff0c;再根据业务对性能的要求&#xff0c;来对API进行性能调优或者扩缩容。通过这些&#xff0c;可以使API稳定地对外提供服务&#xff0c;并且让请求在合理…

java算法day49 | 动态规划part10 ● 121. 买卖股票的最佳时机 ● 122.买卖股票的最佳时机II

121. 买卖股票的最佳时机 class Solution {public int maxProfit(int[] prices) {//1、定义dp数组&#xff0c;表示第i天持股票的状态dp[i][0]表示持有股票dp[i][1]表示不持有股票int[][] dpnew int[prices.length][2];//3、初始化数组dp[0][1]0;dp[0][0]-prices[0];//4、遍历顺…

Linux--进程的概念(二)

目录 一、进程的优先级1.1 基本概念1.2 查看进程优先级1.3 PRI&NI1.4 如何更改进程的优先级1.4.1 用top命令更改进程的nice1.4.2 用renice命令更改进程的nice 1.5 其他概念 二、环境变量2.1 基本概念2.2 常见的环境变量2.3 查看环境变量2.3.1 测试PATH2.3.2 测试HOME2.3.3 …

Android14之智能指针的弱引用、强引用、弱指针、强指针用法区别及代码实例(二百零五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

LTC4054 充电指示灯转灯电路

由于这个芯片只有CHRG# 引脚&#xff0c;不像4056 那样两个引脚能分别接一个LED&#xff0c;要实现充电指示就必须自己整整外围电路。先说明一下&#xff0c;网上常见的这种接法&#xff1a; 一个LED 直连CHRG# 引脚&#xff0c;我试了是不行的&#xff0c;即使充满电&#xff…

【国际会议火热征稿】2024年应用经济学、管理科学与社会国际学术会议(ICAEMSS 2024)

会议简介 2024年应用经济学、管理科学与社会国际学术会议将聚焦应用经济学和管理科学的前沿问题&#xff0c;深入探讨社会变革中的经济管理与科学应用。参会者将分享最新研究成果&#xff0c;交流学术观点&#xff0c;共同探索经济、管理与社会的融合发展之路。本次会议旨在推…

Zotero + Markdown论文工作流

文章目录 关键步骤Zotero Better BibTeXObsidian Citekey Plugin & WrittingPandoc Export 关键步骤 在Zotero中&#xff0c;使用Better BibTex生成.bib文件&#xff0c;用于提取索引信息。由于后续需要使用pandoc将markdown转换为word或者LaTeX&#xff0c;所以Better Bi…

记Kubernetes(k8s):访问 Prometheus UI界面:Warning: Error fetching server time

记Kubernetes&#xff08;k8s&#xff09;&#xff1a;访问 Prometheus UI界面:Warning: Error fetching server time 1、报错详情2、解决3、再次访问 PrometheusUI界面 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、报错详情 Warning:…

Linux启动过程、启动脚本目录介绍及检测思路分析

一、Linux系统启动过程 1、启动流程&#xff1a; Linux系统的启动过程可以分为5个阶段&#xff1a;内核的引导、运行init、系统初始化、建立终端、用户登录系统。 2、init程序的类型&#xff1a; 1&#xff09;SysV&#xff1a;init&#xff0c;CentOS 5之前&#xff0c;配置文…

socuretree远程分支没有同步问题

1、选择命令行模式 2、输入git remote update origin --prune 并回车 git remote update origin --prune 是 Git 命令&#xff0c;用于从远程仓库更新本地分支&#xff0c;并删除本地已经不存在于远程仓库的远程跟踪分支

ADP-2-20+ 信号调节 20MHz-2GHzRF功分器 合路器

ADP-2-20 是一款由Mini-Circuits公司出产的功分器&#xff08;power divider&#xff09;。这款功分器的工作温度规模为-40C至85C&#xff0c;贮存温度规模为-55C至100C。作为分路器&#xff0c;它的电源输入最高可达1W&#xff0c;内部功耗最大为0.125W。假如超越这些限制&…

【Cesium学习笔记】一、加载Cesium并更换天地图底图

【Cesium学习笔记】一、加载Cesium 一、加载Cesium二、用Viewer显示地球三、更换天地图底图 Ps:本教程所有代码于同一个工程中&#xff0c;运行npm run dev默认首页为App.vue&#xff0c;只需替换App.vue的内容即可切换不同页面。 一、加载Cesium 本项目使用nvm管理node版本&…

李沐23_LeNet——自学笔记

手写的数字识别 知名度最高的数据集&#xff1a;MNIST 1.训练数据&#xff1a;50000 2.测试数据&#xff1a;50000 3.图像大小&#xff1a;28✖28 4.10类 总结 1.LeNet是早期成功的神经网络 2.先使用卷积层来学习图片空间信息 3.使用全连接层来转换到类别空间 代码实现…

Al+医学,用这个中文多模态医学大模型帮你看胸片

随着人工智能技术的飞速发展&#xff0c;AI 在医学领域的应用已经成为现实。特别是在医学影像诊断方面&#xff0c;AI 大模型技术展现出了巨大的潜力和价值&#xff0c;但目前针对中文领域医学大多模态大模型还较少。 今天为大家介绍的这款 XrayGLM&#xff0c;就是由澳门理工…