带你打开GCC的大门

news2024/12/23 1:06:28

 

                                                                       START

hi,大家好!

今天带大家了解一下GCC

首先说一句:大写的GCC和小写的gcc不是一个东西呦,下面我们慢慢道来...

1. GCC是什么?

GNU Compiler Collection (GCC)GNU项目开发的编译工具集,支持各种编程语言、硬件架构和操作系统。自由软件基金会(FSF)根据GNU通用公共许可证(GNU GPL)GCC作为免费软件发布。GCCGNU工具链的关键组成部分,也是与GNULinux内核相关的大多数项目的标准编译器。GCC2019年拥有大约1500万行代码,可见功能支持不是一般的全呀,且是现存最大的免费程序之一。作为工具,它在自由软件的发展中发挥了重要作用。

早期GCC的全拼为GNU C Compiler,最初GCC的定位只用于编译C语言。但经过不断的迭代,GCC的功能得到了很大的扩展,它不仅可以用来编译C语言程序,还可以处理CC++Object CJavaFortranPascalModula-3Ada等多种语言等多种编译语言编写的程序。

GCC官网:https://gcc.gnu.org,最新的版本目前已到了:GCC 12.2 (changes)

 

特点

gcc是一个可移植的编译器,支持多种硬件平台。例如ARMX86

gcc它还能跨平台交叉编译。本地编译器,是指编译出来的程序只能够在本地环境进行运行。而gcc编译出来的程序能够在其他平台进行运行。例如嵌入式程序可在x86上编译,然后在arm上运行。

gcc支持多种语言,用于解析不同的语言。

gcc是按模块化设计的,可以加入新语言和新CPU架构的支持。

gcc是自由软件。任何人都可以使用或更改这个软件。

本文着重讲解LinuxGCC常用的重要知识点

GCCgccg++

GCCGNU编译工具集,有编译器、链接器、组装器等,主要用来编译C C++ 语言,也可以编译 Objective-C Objective-C++ 程序。

gccGNU C Compiler)代表的是GNU C语言编译器;g++代表的是GNU C++语言编译器。但是从本质上讲,gcc g++并不是真正的编译器,它们只是GCC里面的两个工具,在编译C/C++程序时,调用真正的编译器对代码进行编译。可以简单地这样理解:gcc工作的时候会调用C编译器;g++工作的时候会调用C++ 编译器。

实际使用中我们习惯使用gcc指令编译C语言程序,用g++指令编译C++代码。需要说的是,gcc指令也可以用来编译C++程序,同样g++指令也可以用于编译C语言程序。

gccg++区别

gcc.c的文件当作C程序,将.cpp的文件当作C++程序

g++.c.cpp的文件都当成C++程序

链接方式:gcc不会自动链接C++的库(比如 STL 标准库),而g++会自动链接C++

预处理器宏:g++会自动添加一些预处理器宏,比如 __cplusplus,但是gcc不会;

gccGCC编译器的通用编译指令,根据文件的后缀名gcc指令可以自行判断出当前程序所用编程语言的类别:

.c:默认以编译C语言程序的方式编译

.cpp:默认以编译C++程序的方式编译

.m:默认以编译Objective-C程序的方式编译

.go:默认以编译Go语言程序的方式编译

当然,我们也可手动指定:-x

gcc -xc file.c 表示以编译C语言代码的方式编译file.c文件

gcc -xc++ file.cpp 则表示以编译C++代码的方式编译file.cpp文件

关于gccg++指令,有其它更多细节方面的区别,在平常编译程序时我们往往坚持一下原则:

对于C语言程序的编译,我们应该使用gcc指令

编译 C++ 程序则推荐使用 g++ 指令

开发纯C语言的程序,可以使用gcc;如果是C/C++混合编程,建议使用g++

2. GCC编译

我们知道计算机只识别机器语言,而我们编写的源代码要想让计算机识别必须翻译成机器语言,也就是生成计算机上识别的可执行的文件,而GCC是如何将我们编写的源代码编译成最终的可执行文件的?

源文件(Source File): 就是我们编写好的源代码文件,源文件本质上是纯文本文件,它的内部并没有特殊格式。我们可以通过源文件的后缀指明该文件中保存的是何种语言的代码,这样程序员更加容易区分,编译器也更加容易识别,它并不会导致该文件的内部格式发生改变。

编译(Compile): 程序语言代码是程序员理解和识别的语言,但是机器只识别机器语言,计算机只认识执行二进制形式的指令,所以需要一个工具,将程序语言代码转换成计算机能够识别的二进制指令,这个工具是一个特殊的软件,叫做编译器(Compiler)。编译器能够识别代码中的句子以及各种特定的格式,并将他们转换成计算机能够识别的二进制形式,这个过程称为编译(Compile)。编译也可以理解为翻译,类似于将英文翻译成中文,它是一个复杂的过程,我们不必关心编译器编译时内部的复杂过程。

链接(Link): C语言代码经过编译以后,并没有生成最终的可执行文件,而是生成了一种叫做目标文件(Object File)的中间文件(或者说临时文件)。目标文件也是二进制形式的,它和可执行文件的格式是一样的。对于Visual C++,目标文件的后缀是.obj;对于GCC,目标文件的后缀是.o。目标文件经过链接(Link)以后才能变成可执行文件。既然目标文件和可执行文件的格式是一样的,为什么还要再链接一次呢,直接作为可执行文件不行吗?不行的!因为编译只是将我们自己写的代码变成了二进制形式,它还需要和系统组件(比如标准库、动态链接库等)结合起来,这些组件都是程序运行所必须的。链接(Link)其实就是一个打包的过程,它将所有二进制形式的目标文件和系统组件组合成一个可执行文件。完成链接的过程也需要一个特殊的软件,叫做链接器(Linker)。

GCC支持的后缀文件

后缀文件类型.cC语言源代码文件.a由目标文件构成的档案库文件.C/.cc/.cxxC++源代码文件.h程序所包含的头文件.i预处理过的C源代码文件.ii预处理过的C++源代码文件.mObjective-C源代码文件.o编译后的目标文件.s汇编语言源代码文件.S预编译的汇编语言源代码文件

GCC的编译过程可以划分为四个阶段:预处理、编译、汇编、链接。

预处理(Pre-Processing):主要处理以“#”开头的命令,生成.i/.ii文件

编译 (Compiling):将.c .i等文件翻译成汇编代码,生成.s/.S文件

汇编 (Assembling):将汇编代码翻译成机器代码,生成.o文件

链接 (Linking):将生成的多个目标文件(.o文件)连接起来,生成可执行文件

下面我们通过一个简单例程来分析GCC的整个执行过程:

#include <stdio.h>



int main(int argc, char const *argv[])

{

    /* code */

    printf("hello GCC\n");



    return 0;

}

1.预处理阶段

gcc -E main.c -o main.i

 预处理过程主要处理源代码中以#开头的预编译指令,处理#include和#define,它把#include包含进来的.h文件插入到#include所在的位置,把源程序中使用到的用#define定义的宏用实际的字符串代替

主要处理如下:

将所有的#define宏定义删除,并且展开所有的宏定义

处理所有条件编译指令,如#if#ifdef

处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。该过程递归进行,及被包含的文件可能还包含其他文件

删除所有的注释// /**/

添加行号和文件标识,如#2 “main.c” 2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号信息

保留所有的#pragma编译器指令,因为编译器须要使用它们

2.编译阶段

gcc -S main.i -o main.s

 编译阶段,在这个阶段中,GCC首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,GCC把代码翻译成汇编语言。

让我们来看一下生成的汇编文件main.s的内容:

 

3.汇编阶段

gcc -c main.s -o main.o

汇编阶段把*.s文件翻译成二进制机器指令文件*.o,也就是说将汇编代码转变成机器可以执行的命令

4.链接阶段

gcc main.o -o main

 链接器ld将各个目标文件组装在一起,解决符号依赖,库依赖关系,并生成可执行文件。通过本例程通俗的说就是:在这个程序中并没有定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现”printf”函数的呢?答案是系统把这些函数实现都被做到名为libc.so.6的库文件中去了,在没有特别指定时,GCC会到系统默认的搜索路径”/usr/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数”printf”了,而这也就是链接的作用

默认情况下, GCC链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,如果需要使用静态链接库可以在编译时加上-static选项,强制使用静态链接库。由于动态库节省空间,linux下进行链接的缺省操作是首先连接动态库。

一般头文件或库文件的位置在:

/usr/include及其子目录下的include文件夹

/usr/local/include及其子目录下的include文件夹

/usr/lib

/usr/local/lib

/lib

静态库链接时搜索路径顺序:

ld会去找GCC命令中的参数-L

再找gcc的环境变量LIBRARY_PATH

再找内定目录 /lib /usr/lib /usr/local/lib

动态链接时、执行时搜索路径顺序:

编译目标代码时指定的动态库搜索路径

环境变量LD_LIBRARY_PATH指定的动态库搜索路径

配置文件/etc/ld.so.conf中指定的动态库搜索路径

默认的动态库搜索路径/lib

默认的动态库搜索路径/usr/lib

库的搜索路径遵循几个搜索原则:从左到右搜索-I -l指定的目录,如果在这些目录中找不到,那么GCC会从由环境变量指定的目录进行查找。头文件的环境变量是C_INCLUDE_PATH,库的环境变量是LIBRARY_PATH,如果还是找不到,那么会从系统指定指定的目录进行搜索。

相关环境变量:LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径 LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径

通过链接阶段后就生成了我们需要的可执行程序,我们就可以执行可执行程序了

GCC参数说明

GCC编译时参数众多,一下是常用的参数说明:

-C

在预处理的时候, 不删除注释信息, 一般和-E使用, 有时候分析程序,用这个很方便的。

-M

生成文件关联的信息。包含目标文件所依赖的所有源代码你可以用 gcc -M hello.c 来测试一下,很简单。

-MM

和上面的那个一样,但是它将忽略由 #include<file> 造成的依赖关系。

-MD

-M相同,但是输出将导入到.d的文件里面

-MMD

-MM 相同,但是输出将导入到 .d 的文件里面。

-Wa,option

此选项传递 option 给汇编程序; 如果 option 中间有逗号, 就将 option 分成多个选项, 然 后传递给会汇编程序。

-Wl.option

此选项传递 option 给连接程序; 如果 option 中间有逗号, 就将 option 分成多个选项, 然 后传递给会连接程序。

-llibrary

指定编译的时候使用的库

例子用法:

gcc -lcurses hello.c使用 ncurses 库编译程序

-Ldir

制定编译的时候,搜索库的路径。比如你自己的库,可以用它制定目录,不然编译器将只在标准库的目录找。这个dir就是目录的名称。

-O0 -O1 -O2 -O3

编译器的优化选项的 4 个级别,-O0 表示没有优化, -O1 为默认值,-O3 优化级别最高。

-g

只是编译器,在编译的时候,产生调试信息。

-gstabs

此选项以 stabs 格式声称调试信息, 但是不包括 gdb 调试信息。

-gstabs+

此选项以 stabs 格式声称调试信息, 并且包含仅供 gdb 使用的额外调试信息。

-ggdb

此选项将尽可能的生成 gdb 的可以使用的调试信息。

-static

此选项将禁止使用动态库,所以,编译出来的东西,一般都很大,也不需要什么动态连接库,就可以运行。

-share

此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库。

-x language filename

设定文件所使用的语言, 使后缀名无效, 对以后的多个有效。也就是根据约定 C 语言的后缀名称是 .c 的,而 C++ 的后缀名是 .C 或者 .cpp, 如果你很个性,决定你的 C 代码文件的后缀名是 .pig 哈哈,那你就要用这个参数, 这个参数对他后面的文件名都起作用,除非到了下一个参数的使用。可以使用的参数吗有下面的这些:'c', 'objective-c', 'c-header', 'c++', 'cpp-output', 'assembler', 'assembler-with-cpp'

看到英文,应该可以理解的。

例子用法:gcc -x c hello.pig

-x none filename

关掉上一个选项,也就是让gcc根据文件名后缀,自动识别文件类型 。

例子用法:gcc -x c hello.pig -x none hello2.c

-c

只激活预处理,编译,和汇编,也就是他只把程序做成obj文件

例子用法:

gcc -c hello.c他将生成 .o obj 文件

-S

只激活预处理和编译,就是指把文件编译成为汇编代码。

例子用法:

gcc -S hello.c他将生成 .s 的汇编代码,你可以用文本编辑器察看。

-E

只激活预处理,这个不生成文件, 你需要把它重定向到一个输出文件里面。

例子用法:

gcc -E hello.c > pianoapan.txtgcc -E hello.c | more慢慢看吧, 一个 hello word 也要与处理成800行的代码。

-o

制定目标名称, 默认的时候, gcc 编译出来的文件是 a.out, 很难听, 如果你和我有同感,改掉它, 哈哈。

例子用法:

gcc -o hello.exe hello.c gcc -o hello.asm -S hello.c

-pipe

使用管道代替编译中临时文件, 在使用非 gnu 汇编工具的时候, 可能有些问题。

gcc -pipe -o hello.exe hello.c

-ansi

关闭 gnu c中与 ansi c 不兼容的特性, 激活 ansi c 的专有特性(包括禁止一些 asm inline typeof 关键字, 以及 UNIX,vax 等预处理宏)。

-fno-asm

此选项实现 ansi 选项的功能的一部分,它禁止将 asm, inline typeof 用作关键字。

-fno-strict-prototype

只对 g++ 起作用, 使用这个选项, g++ 将对不带参数的函数,都认为是没有显式的对参数的个数和类型说明,而不是没有参数。

gcc 无论是否使用这个参数, 都将对没有带参数的函数, 认为城没有显式说明的类型。

-fthis-is-varialble

就是向传统 c++ 看齐, 可以使用 this 当一般变量使用。

-fcond-mismatch

允许条件表达式的第二和第三参数类型不匹配, 表达式的值将为 void 类型。

-funsigned-char -fno-signed-char-fsigned-char -fno-unsigned-char

这四个参数是对 char 类型进行设置, 决定将 char 类型设置成 unsigned char(前两个参数)或者 signed char(后两个参数)

-include file包含某个代码,简单来说,就是便以某个文件,需要另一个文件的时候,就可以用它设定,功能就相当于在代码中使用 #include<filename>

例子用法:

gcc hello.c -include /root/pianopan.h -imacros file

file 文件的宏, 扩展到 gcc/g++ 的输入文件, 宏定义本身并不出现在输入文件中。

-Dmacro

相当于 C 语言中的 #define macro

-Dmacro=defn

相当于 C 语言中的 #define macro=defn

-Umacro

相当于 C 语言中的 #undef macro

-undef

取消对任何非标准宏的定义

-Idir

在你是用 #include "file" 的时候, gcc/g++ 会先在当前目录查找你所制定的头文件, 如果没有找到, 他回到默认的头文件目录找, 如果使用 -I 制定了目录,他会先在你所制定的目录查找, 然后再按常规的顺序去找。

对于 #include<file>, gcc/g++ 会到 -I 制定的目录查找, 查找不到, 然后将到系统的默认的头文件目录查找 。

-I-

就是取消前一个参数的功能, 所以一般在 -Idir 之后使用。

-idirafter dir

-I 的目录里面查找失败, 讲到这个目录里面查找。

-iprefix prefix -iwithprefix dir

一般一起使用, -I 的目录查找失败, 会到 prefix+dir 下查找

-nostdinc

使编译器不再系统默认的头文件目录里面找头文件, 一般和 -I 联合使用,明确限定头文件的位置。

-nostdin C++

规定不在 g++ 指定的标准路经中搜索, 但仍在其他路径中搜索, 此选项在创 libg++ 库使用 。

-traditional

试图让编译器支持传统的C语言特性

小结

好了,小伙伴们,这次就先到这里吧。希望本文对小伙伴们有所帮助,喜欢的记得点赞、分享

                                                                       

                                                                        END

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

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

相关文章

abaqus和ansys做仿真哪个更好

当你要模拟仿真一个机械模型时&#xff0c;通常会听到ABAQUS或ANSYS&#xff0c;最常见的问题是哪个更好&#xff1f;无论是工程设计师还是初学者&#xff0c;通常会问这个问题或类似的问题。在本文中介绍了 Abaqus 与 Ansys&#xff0c;您将了解这些问题的答案。 1-ANSYS&…

数据库8之嵌套查询

上一篇文章讲到连接查询&#xff0c;连接查询就是一个一个去查找相匹配的行&#xff0c;再返回给用户看。当我们数据量少的时候我们用连接查询没有太大问题&#xff0c;可是&#xff0c;当数据量大的时候&#xff0c;连接查询效率显然不高。这个时候我们可以用嵌套查询&#xf…

Oracle跨服务器取数——DBlink 初级使用

前言 一句话解释DBlink是干啥用的 实现跨库访问的可能性. 通过DBlink我们可以在A数据库访问到B数据库中的所有信息,例如我们在加工FDS层表时需要访问ODS层的表,这是就需要跨库访问 一、DBlink的分类 private&#xff1a;用户级别&#xff0c;只有创建该dblink的用户才可以使…

Maven 下载及配置详细步骤

1、Maven 下载 Maven 官网地址:https://maven.apache.org/download.cgi(opens new window) 进入 Maven 官网,点击 archives 下载版本 3.6.2 找到下载的压缩包并解压

传统协议大比拼之IP协议

文章目录 一、引言二、IP协议的基本特点2.1 IP协议的作用和基本功能2.2 地址管理手动分配IP动态主机配置协议(DHCP)网络地址转换(NAT)IPv6 2.2 路由选择RIP(距离向量型的协议)OSPF(链路状态类型协议)BGP(边界网关协议) 2.3 IP协议的特点&#xff1a; 三、IP地址的组成3.1 IP地址…

kong(6):身份认证

1 Basic Auth身份认证配置 Basic Auth插件 # 在服务上配置插件 curl -X POST http://127.0.0.1:8001/services/{service}/plugins --data "namebasic-auth" --data "config.hide_credentialstrue"#在路由上配置插件 curl -X POST http://127.0.0.1:8001/…

上海亚商投顾:沪指全天震荡微跌 新能源赛道股集体反弹

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 大小指数今日走势分化&#xff0c;沪指探底回升小幅下跌&#xff0c;创业板指盘中涨超2%&#xff0c;午后涨幅有所…

基于centos7:Harbor-2.7.2部署和安装教程

基于centos7&#xff1a;Harbor-2.7.2部署和安装教程 1、软件资源介绍 Harbor是VMware公司开源的企业级DockerRegistry项目&#xff0c;项目地址为https://github.com/vmware/harbor。其目标是帮助用户迅速搭建一个企业级的Dockerregistry服务。它以Docker公司开源的registry…

数据结构3:栈和队列

目录 1.栈 1.1栈的概念及结构 ​1.2栈的实现 2.队列 2.1队列的概念及结构 2.2队列的实现 2.3循环队列 ​3.栈和队列的面试题 4.概念选择题 1.栈 1.1栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据…

深入探讨Linux驱动开发:驱动介绍与hello驱动实例

文章目录 前言一、Linux驱动介绍1.用户态和内核态2.内核功能介绍3.驱动程序介绍 二、驱动程序分类与注意事项1.驱动程序分类2.内核驱动开发注意事项 三、hello驱动开发1.驱动模块2.模块加载和卸载函数3.编写hello模块4.Makefile 四、运行测试总结 前言 通过之前对于IGKBoard开…

Python操作PostgreSQL数据库

个人简介&#xff1a;一个从会计转行数据分析师的三旬老汉 擅长领域&#xff1a;数据分析、数据仓库、大数据 博客内容&#xff1a;平时会将自己工作中遇到的问题进行归纳总结&#xff0c;分享给各位小伙伴&#xff0c;意在帮助大家少加班、不掉发&#xff0c;让我们相互学习&a…

Timer0/1设置时钟计算中断时间

时钟一般分为外部晶振时钟和内部时钟&#xff0c;相对而说&#xff0c;外部晶振时钟的精准度比内部系统时钟高&#xff0c;时间计算的更准。除非产品需要一般都不会用外部晶振时钟&#xff0c;因为好的东西贵啊&#xff0c;成本高。 本文主要介绍如何利用时钟设置Timer0/1&…

0603基础使用(二)-react路由-react

文章目录 3 NavLink简单封装4 switch的使用5 解决样式丢失问题6 路由的模糊匹配和严格匹配7 Redirect结语 3 NavLink简单封装 在之前使用NavLink标签时&#xff0c;只有2个&#xff0c;代码如下&#xff1a; <NavLink activeClassName"g2zh" classNamelist-grou…

C#:如何用分部类将一个大文件改为多个小文件?

很多时候我们会发现&#xff0c;写来写去&#xff0c;一个文件慢慢就变得很大了&#xff0c;行数过千基本上就维护比较困难。 将公共代码模块化&#xff0c;可以减少一些代码&#xff0c;也是非常有效的。 那还有其它办法吗&#xff1f; 用 分部类 可以解决。 下面是简单的…

eBPF的发展演进---从石器时代到成为神(三)

4. 内在驱动 由以上简要的回顾和梳理可见&#xff0c;内核开发者们所不断寻找的是一种充分表达能力的动态机制&#xff0c;进而打破内核和用户态的壁垒&#xff08;至少在逻辑层面&#xff09;&#xff0c;从而实现一种自由、直接的需求实现。技术成为内核开发者们锋利的工具&…

UE4/5 行为树使用教程

使用行为树首先需要保证目标蓝图是继承自Character基类。然后根据本文下面的流程操作即可。 1.创建AIController 首先需要在角色自身蓝图之外创建一个新的蓝图&#xff0c;继承自AIController&#xff1a; 2.挂载AIController 找到角色自身蓝图类设置中的Pawn一栏&#xf…

云LIS系统是什么?云LIS系统的功能有哪些?

云LIS系统源码 C#医学检验云LIS平台源码 云LIS系统是什么&#xff1f; 云LIS是为区域医疗提供临床实验室信息服务的计算机应用程序&#xff0c;可协助区域内所有临床实验室相互协调并完成日常检验工作&#xff0c;对区域内的检验数据进行集中管理和共享&#xff0c;通过对质量…

C#探索之路(7):初探LitJson库并了解其中json的解析原理与处理报错

C#探索之路(7)&#xff1a;使用LitJson库解析数据抛出的异常错误修复指南与途径Tips 对Json格式的了解程度一定程度上影响了解决JSON相关问题的效率&#xff1b; 文章目录 C#探索之路(7)&#xff1a;使用LitJson库解析数据抛出的异常错误修复指南与途径Tips1、初步较为系统的去…

TryHackMe-Mnemonic(boot2root)

Mnemonic I hope you have fun. 端口扫描 循例nmap FTP枚举 尝试anonymous Web枚举 进80 gobuster扫 对着webmasters再扫一下 对着backups继续扫 下载zip文件&#xff0c;发现有密码 zip2john john直接爆 查看note.txt, 给出了ftpuser hydra直接爆ftp 进到ftp 用wget下载所…

VR全景图片,探究VR全景图片为何如此受欢迎?

随着科技的不断进步&#xff0c;虚拟现实技术逐渐渗透到我们的日常生活中&#xff0c;为我们带来了许多前所未有的体验和乐趣。而其中&#xff0c;VR全景图片作为一种基于虚拟现实技术的图片展示形式&#xff0c;不仅在旅游、房地产、教育等领域得到了广泛的应用&#xff0c;也…