Linux:编译器 - gcc

这篇具有很好参考价值的文章主要介绍了Linux:编译器 - 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中,有如下代码:

Linux:编译器 - gcc,Linux,linux


预处理

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

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

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

gcc -E test.c -o test.i

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

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

Linux:编译器 - gcc,Linux,linux

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

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

Linux:编译器 - gcc,Linux,linux

如果我们把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选项用于指定一个宏。

看到以下现象:
Linux:编译器 - gcc,Linux,linux

第一次-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内部:

Linux:编译器 - gcc,Linux,linux

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


汇编

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

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

gcc -c test.c -o test.o

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

test.o内部:

Linux:编译器 - gcc,Linux,linux

可以看到,其内部都是一些乱码,因为我是用的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

输出结果:

Linux:编译器 - gcc,Linux,linux

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

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

Linux:编译器 - gcc,Linux,linux

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

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


动态库与静态库

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

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

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

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

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

动静态库的后缀

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

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

gcc -o test1.exe test.c

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

Linux:编译器 - gcc,Linux,linux

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

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

Linux:编译器 - gcc,Linux,linux

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

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

gcc -o test2.exe test.c -static

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

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

Linux:编译器 - gcc,Linux,linux

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

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

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

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

Linux:编译器 - gcc,Linux,linux

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

使用ldd

Linux:编译器 - gcc,Linux,linux

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

再用file

Linux:编译器 - gcc,Linux,linux

statically linked字段说明这是一个静态链接的可执行文件。文章来源地址https://www.toymoban.com/news/detail-850509.html


到了这里,关于Linux:编译器 - gcc的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux编译器 gcc与g++

    程序的编译过程: 1、 预处理 (头文件包含、消除注释、宏定义替换) 2、 编译 (将语言替换成汇编代码) 3、 汇编 (将汇编指令转换为二进制指令) 4、 链接 (合并段表、符号表合并及重定位) 我们可以通过gcc工具实现程序的编译过程: 2.1 预处理 预处理会完成:①头

    2023年04月18日
    浏览(65)
  • Linux编译器——gcc/g++使用

    前言:  在上一篇,我们学习了关于文本编辑器 vim 的全部知识,今天给大家带来的是关于Linux编译器—gcc/使用的详细介绍。 本文目录  (一)温习程序的产生的过程 1、前言 2、程序的产生过程 3、🌜初步认识 gcc🌛 a) gcc的基本概念 b)gcc的基本特点 4、使用方法💻 (二)

    2023年04月17日
    浏览(45)
  • Linux--编译器-gcc/g++使用

    目录 前言 1.看一段样例  2.程序的翻译过程 1.第一个阶段:预处理 2.第二个阶段:编译 3.第三个阶段:汇编 4.第四个阶段:链接 3.程序的编译为什么是这个样子? 4. 关于编译器 5.链接(动静态链接) 1.首先,我们来看一段样例(见一下) 2.见完之后,我们来看一下程序的翻译

    2024年02月20日
    浏览(53)
  • 【Linux】编译器-gcc/g++使用

    个人主页 : zxctscl 文章封面来自:艺术家–贤海林 如有转载请先通知 在之前已经分享了 【Linux】vim的使用,这次来看看在云服务器上的编译器gcc。 我们先写一段简单的代码: 当我们进行编译的时候: 发现根本就编译不了。 这个是因为编译器版本的问题: 查看编译器的版

    2024年03月11日
    浏览(120)
  • Linux的学习之路:6、Linux编译器-gcc/g++使用

    本文主要是说一些gcc的使用,g++和gcc使用一样就没有特殊讲述。 目录 摘要 一、背景知识 二、gcc如何完成 1、预处理(进行宏替换) 2、编译(生成汇编) 3、汇编(生成机器可识别代码 4、链接(生成可执行文件或库文件) 5、函数库 6、静态库和动态库 7、gcc选项 三、思维导图

    2024年04月23日
    浏览(41)
  • Linux的编译器——gcc/g++(预处理、编译、汇编、链接)

    前言: 本文主要认识与学习 Linux 环境下常用的编译器—— gcc (编译 C 代码)/ g++ (编译 C++ 代码)的常用指令等、程序实现的两大环境、动态库与静态库的理解等。 任何一个 C 程序的实现都要经过 翻译环境 与 执行环境 。 在翻译环境中又分为4个部分, 预编译、编译、汇

    2024年02月13日
    浏览(34)
  • [linux初阶][vim-gcc-gdb] TwoCharter: gcc编译器

    目录 一.Linux中gcc编译器的下载与安装 二.使用gcc编译器来翻译 C语言程序  ①.编写C语言代码 ②翻译C语言代码 a.预处理 b.编译  c.汇编  d.链接 ③.执行Main 二进制可执行程序(.exe文件)  三.总结  使用yum命令(相当于手机上的应用市场)来进行安装.  下载完毕之后,使用 which gc

    2024年04月10日
    浏览(47)
  • 如何在 Linux 上安装 C 和 GCC 编译器?

    Linux 是一组开源的类 UNIX 操作系统,Ubuntu 是一种基于 Linux 的操作系统,通常用于运行基于 Linux 的应用程序。要在 Linux 上安装 C 并在 Ubuntu 上构建和运行 C 程序文件,我们需要安装 GCC 编译器。在 Ubuntu 存储库中,GCC 编译器是我们需要安装的 构建基本 包的一部分。 我们知道

    2024年04月13日
    浏览(38)
  • 【Linux环境基础开发工具】编译器-gcc/g++

    上一篇博客, 我们学习了vim编辑工具,学会了怎么写代码, 这篇文章,我将分享代码该怎么编译的问题。 目录 写在前面: 1. gcc和g++介绍 2. gcc是如何编译程序的 1. 预处理 2. 编译 3. 汇编 4. 链接 3. gcc的选项介绍 4. 我们使用的函数是哪来的 5. 我们的.o文件和库是如何链接的?

    2024年02月10日
    浏览(40)
  • 【Linux】Linux编译器 gcc/g++的使用&&初识动静态链接库

    ​ ​📝个人主页:@Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:Linux 🎯 长路漫漫浩浩,万事皆有期待 上一篇博客:【Linux】Linux环境基础工具的基本使用及配置(yum、vim) 在上一篇 Linux 博客中,我们讲解了 vim 编辑器的使用,可以在 Linux 上写

    2024年02月11日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包