一文入门gcc

2024-06-15 23:20
文章标签 入门 一文 gcc

本文主要是介绍一文入门gcc,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

今天我们来玩玩gcc。

是因为突然发现ESP-IDF用的是CMake,要了解CMake最好就要先学习Makefile有个基础,学习Makefile最好就要先熟悉gcc,所以就有了今天这篇文章。

首先我们要明确一个问题,那就是gcc/g++是什么,它们有什么用。

gcc 是 GNU Compiler Collection(GNU 编译器集合)的缩写,是一个用于编程语言的编译器,特别是 C、C++、Fortran、Objective-C、Objective-C++、Ada、Go 以及其他一些语言。它最初是为 GNU 操作系统开发的,但如今已被广泛应用于各种 Unix-like 系统(包括 Linux)和其他操作系统(如 Windows,通过 MinGW 或 Cygwin)。

gcc 的主要用途是将源代码(例如 C 或 C++ 代码)编译成机器代码(通常是可执行文件或库)。它支持多种优化选项和调试选项,使得开发人员能够根据需要调整编译过程。

简单来说gcc就是编译器,它可以将我们写的.c文件变成.exe的可执行文件(当然了,不止.c文件,可执行文件的后缀也不一定是.exe)。

而g++也属于gcc,不过是专门为了编译C++而产生的特化版本。

由于大家初学C语言的时候,大概率是看着教学视频学的,并且视频里一般都是教大家下载IDE,在IDE里进行编程的,因此我们不需要去关注具体是如何使用编译器去编译我们的c文件的。但是到了Linux环境下,就没有那么轻松了(虽然Linux也可以下载安装IDE,但是很多情况下我们用的不是有图形界面的Linux,而是命令行,这样也比较帅对叭),我们需要手动敲命令去对我们写好的程序进行编译。

安装gcc

那么第一步,我们需要先安装上gcc。

我们打开虚拟机,或者你有服务器可以远程连接上也可以,再或者你远程连接你的虚拟机也OK。

我这边以Ubuntu为例(因为手头上安装的虚拟机是Ubuntu的)

我们在命令行里输入下面这个命令

sudo apt-get install gcc

如果成功下载,那么我们可以直接进入下一步。

如果下载不了,显示没有这个软件包,那么我们需要加个源。

执行下面的命令去修改源列表文件。

vim /etc/apt/sources.list

添加一行下面这个内容(不会用vim的小伙伴在上面那个命令执行过后按下i键,然后用上下左右键移动到文件的最下面,然后将下面的复制进去,接着按下esc键,再打个 :冒号,然后输入qw保存退出,如果显示权限不够那就出去改个权限。)。

deb [arch=amd64] http://archive.ubuntu.com/ubuntu focal main universe

下一步更新源。

sudo apt-get update

更新完之后下载gcc。

sudo apt-get install gcc

再输入下面命令查看gcc版本,如果能正常看到那么就表示我们下载成功。下载g++的话只需要把上面下载命令中的gcc替换成g++即可。

gcc -v

编译的流程

接下来我们来了解一下.c文件是如何变成.exe文件的。

一共是四个步骤,分别是预处理,汇编,编译,链接。曾经VS帮我们做好了一切,如今我们需要还我们欠下的债了,深刻理解这四个步骤也有助于我们理解我们自己写下的代码(没错,就算是你自己写的代码你也不一定能够彻底明白,反正我上星期写的代码我这星期已经看不懂了)。

预处理

预处理这一步主要会做下面这些步骤。

  • 代码包含:预处理器处理#include指令,这通常意味着将头文件的内容直接插入到源文件中。
  • 宏替换:所有#define定义的宏都会被展开,即在代码中进行相应的文本替换。
  • 条件编译:处理如#if#ifdef等条件编译指令,这些指令通常用于在不同环境下编译不同的代码路径。
  • 注释删除:预处理器会移除所有注释部分,这些注释对编译后的代码执行没有影响。
  • 生成中间文件:预处理过程后通常会生成一个中间文件(如.i文件),供后续编译过程使用

在gcc中预处理的选项是-E,我们只需要使用gcc -E 这个命令就可以对我们写的.c文件进行预处理了。但是光预处理还没有,我们需要把预处理的结果保存起来,这时候就需要使用-o选项来指定文明存放预处理结果的文件。

使用例子如下。其中test.c是写的.c文件,test.i是预处理结果

 gcc -E test.c -o test.i

然后test.c的内容是下面这样的。

很简单的内容,包含了个头文件,使用宏定义代替了一个打印语句。在main函数里使用宏定义并且用了个打印语句。

经过预编译之后我们得到test.i文件。实际上可以叫任何名字,只需要在上面预编译的命令中修改即可,甚至后缀都不一定非得是 .i ,在Linux中后缀是没有实际作用的,它唯一的作用是对于让人可以通过后缀判断出这个文件是什么类型的,而对于Linux来说,后缀是无所谓有无所谓无的东西。

虽然名字可以随意,但是我们约定俗成的命名规则就是保留.c文件的名字,然后修改后缀。

接着我们看看预处理后得到的test.i是什么内容。

原本很简单的一段代码,经过预处理之后变成八百多行了。

文件开头我们看不懂,我们拉到最下面。

主要看最后面的main函数,里面的宏定义被替换成了printf语句。并且注释也没有了。还将我们的头文件展开了,没错,前面差不多八百行代码就是原文件中的#include <stdio.h>展开后的结果。

这就是预处理。

编译

编译这个步骤会对上个步骤得到的 .i 文件做以下处理,最终生成汇编文件。

  • 词法分析:编译器首先识别出源代码中的每个单词,如变量名、运算符等。
  • 语法分析:之后,编译器解析这些单词组成的语法结构,如表达式、控制结构等。
  • 语义分析:确保所有变量都被正确声明,且操作都是合理的,例如类型检查。
  • 优化:编译器会对中间代码进行优化,以提升执行效率,去除不必要的操作。
  • 生成汇编代码:最终,编译器生成对应的汇编代码文件(如.s文件),此代码是机器语言的低级表示。

gcc中汇编的选项是 -S ,得到的汇编文件的后缀一般我们都给 .s ,所以使用的命令如下。

gcc -S test.i -o test.s

我们可以来查看一下生成的汇编文件。

虽然还是很多,但是已经压缩成48行了。汇编就已经是很接近底层的了。

汇编

  • 代码翻译:汇编器将汇编代码转换成机器码,生成目标文件(如.o文件)。
  • 符号引用解决:这一阶段还会处理代码中的符号引用,确定所有引用的目标地址。

下一步就是汇编了,这有点小迷惑人,因为编译的结果是汇编文件,而汇编的结果是目标文件。

gcc中执行汇编步骤的选项是 -c ,示例命令如下。

gcc -c test.s -o test.o

输入的是 .s 文件,输出的是 .o文件。这一步就将我们的汇编转成机器码了,当我们再去查看的时候就不是我们能够看懂的了(虽然我估计大部分人连汇编都看不懂了(包括我))。

还有个需要说的,就是汇编这一步,可以直接从 .c到 .o,也就是可以跳过前两步直接到第三步。 

链接

最后一步链接,就可以生成可执行文件了。

  • 合并代码:链接器将所有的目标文件以及它们所需要的库文件合并,形成最终的可执行文件。
  • 解析外部引用:解决不同文件之间相互调用的函数或者变量地址解析问题。
  • 生成可执行文件:最终输出一个可执行的程序文件,该文件可以直接在操作系统上运行。

gcc中链接这一步骤不需要选项,也就是像下面这样。

gcc test.o -o test

直接输出没有后缀的可执行文件(可以让它的后缀为exe方便理解,但是在Linux中我们一般直接不要后缀)

那么我们就已经有了一个可执行文件了,应该怎么执行呢?

./test 

像上面这样一个点加个斜杠再加上可执行文件的名字就可以啦。

结果是下面这样的。

可能有小伙伴会说这个gcc也太麻烦了,我还是用IDE吧。

那其实我们要可执行文件的话可以不用按照这四个步骤一步步来,现实中我们不能一步登天,但是在gcc里可以,我们可以直接执行下面这个命令,直接从 .c 编译成可执行文件。

gcc test.c -o test

这样就简单很多了。

上面就是gcc最基本的用法了,懂得上面这几步之后,理论上去学个Makefile什么的问题不大。

不过我们可以再深入了解了解gcc,这个拥有着强大功能的工具不可能就做这么点事对吧。

指定头文件目录

假如我们现在写了一个含有功能函数的文件(或者是下载来的),那么我们在主函数要用对应的函数,那么就需要包含头文件对吧。

现在我在main.c文件的同级目录下专门建了个文件夹存放工具文件。并且我在main.c里包含了这些文件。像下面这样。

然后我们再编译就会发现gcc找不到我们的头文件。

这时候就要请出我们gcc指定头文件的选项 -I 了。

gcc -I ./my_tools test.c -o test

这样包含头文件就没问题了,但是又出了新的问题,那就是找不到函数定义了。

这是因为我们需要把被包含的文件一起放进来编译。

这样就没问题了,可是这样子写的话会非常麻烦,光是包含一个头文件就要加这一长串,万一我包含了很多呢?这就要请出我们的Makefile了(下一篇也可能是下n篇文章我们再说)。

总之我们先知道指定头文件目录的这个选项即可。

设置优化级

-O选项可以设置优化级,这边是大写的O,别和之前小写的o混一起了。

GCC编译器提供了多种优化级别,通过使用-O后跟一个数字(从0到3),可以指定希望GCC应用的优化等级。

  1. 优化级别0 (-O0)

    • 关闭所有编译优化。这是默认的编译设置,主要用于调试阶段,因为优化可能会改变程序的执行方式,从而影响调试的准确性。
  2. 优化级别1 (-O1)

    • 开启基本优化。这一级别的优化包括将常用值分配给寄存器、消除无用的代码等简单优化。这是在不影响调试的前提下提高程序性能的一个平衡选择。
  3. 优化级别2 (-O2)

    • 开启中级优化。除了包含-O1中的所有优化外,还包括了更复杂的优化如分支预测、循环展开、内联函数等。这可以显著提高程序的运行速度,但可能会增加编译时间和最终可执行文件的大小。
  4. 优化级别3 (-O3)

    • 开启高级优化。它包括-O2中的所有优化,并尝试进一步执行如函数内联扩展等更高级的优化技术。此级别适用于当程序性能是关键考虑因素时,但需要注意这可能会使编译时间变长,并且在某些情况下可能影响程序的稳定性或可预测性。
  5. 额外优化级别 (-Os)

    • -Os:专注于生成尽可能小的可执行文件大小。适用于嵌入式系统和那些对程序大小有严格要求的场景。

默认的优化级别是O0,一般情况下我们不动它的。

添加宏定义

这个会用的多一点。

-D后接我们要在编译时候注册的宏。

我现在修改一下我们之前的test.c文件。

然后我们直接编译,运行的结果是这样的。

当我们使用了-D选项,则是下面这样的效果。

通过这样的小例子,相信大家就对-D有了简单的认识了。

今天在这边就介绍到这边了,对于我们后续学习Makefile来说是足够的。

感觉不过瘾,想更深入了解gcc的小伙伴可以直接

man gcc

也可以去查官方文档。

GCC online documentation- GNU Projecticon-default.png?t=N7T8https://gcc.gnu.org/onlinedocs/

这篇关于一文入门gcc的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1064868

相关文章

从入门到精通MySQL联合查询

《从入门到精通MySQL联合查询》:本文主要介绍从入门到精通MySQL联合查询,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下... 目录摘要1. 多表联合查询时mysql内部原理2. 内连接3. 外连接4. 自连接5. 子查询6. 合并查询7. 插入查询结果摘要前面我们学习了数据库设计时要满

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

一文详解Git中分支本地和远程删除的方法

《一文详解Git中分支本地和远程删除的方法》在使用Git进行版本控制的过程中,我们会创建多个分支来进行不同功能的开发,这就容易涉及到如何正确地删除本地分支和远程分支,下面我们就来看看相关的实现方法吧... 目录技术背景实现步骤删除本地分支删除远程www.chinasem.cn分支同步删除信息到其他机器示例步骤

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决

一文详解Java Stream的sorted自定义排序

《一文详解JavaStream的sorted自定义排序》Javastream中的sorted方法是用于对流中的元素进行排序的方法,它可以接受一个comparator参数,用于指定排序规则,sorte... 目录一、sorted 操作的基础原理二、自定义排序的实现方式1. Comparator 接口的 Lam

从入门到精通MySQL 数据库索引(实战案例)

《从入门到精通MySQL数据库索引(实战案例)》索引是数据库的目录,提升查询速度,主要类型包括BTree、Hash、全文、空间索引,需根据场景选择,建议用于高频查询、关联字段、排序等,避免重复率高或... 目录一、索引是什么?能干嘛?核心作用:二、索引的 4 种主要类型(附通俗例子)1. BTree 索引(

Redis 配置文件使用建议redis.conf 从入门到实战

《Redis配置文件使用建议redis.conf从入门到实战》Redis配置方式包括配置文件、命令行参数、运行时CONFIG命令,支持动态修改参数及持久化,常用项涉及端口、绑定、内存策略等,版本8... 目录一、Redis.conf 是什么?二、命令行方式传参(适用于测试)三、运行时动态修改配置(不重启服务

MySQL DQL从入门到精通

《MySQLDQL从入门到精通》通过DQL,我们可以从数据库中检索出所需的数据,进行各种复杂的数据分析和处理,本文将深入探讨MySQLDQL的各个方面,帮助你全面掌握这一重要技能,感兴趣的朋友跟随小... 目录一、DQL 基础:SELECT 语句入门二、数据过滤:WHERE 子句的使用三、结果排序:ORDE

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

一文详解MySQL如何设置自动备份任务

《一文详解MySQL如何设置自动备份任务》设置自动备份任务可以确保你的数据库定期备份,防止数据丢失,下面我们就来详细介绍一下如何使用Bash脚本和Cron任务在Linux系统上设置MySQL数据库的自... 目录1. 编写备份脚本1.1 创建并编辑备份脚本1.2 给予脚本执行权限2. 设置 Cron 任务2