Linux 编译器 gcc/g++

news/2024/11/9 2:10:44/

本文已收录至《Linux知识与编程》专栏!
作者:ARMCSKGT
演示环境:CentOS 7 

   


目录

前言

正文

gcc/g++常用命令

自定义可执行程序名命令-o

预处理指令-E

编译指令-S

汇编指令-c

链接指令gcc

命令巧记口诀

 链接库

动态库-动态链接

静态库-静态链接

动静态库对比

其他指令

声明与定义分离的编译指令

C99标准编译指令

gcc和g++的联系与区别

 最后


前言

在Windows环境下,我们我们使用VS编译器可以写代码然后运行编译代码所成的程序,但在Linux系统下,我们写的C/C++代码需要通过gcc(C语言)和g++(C++语言)编译器才能生成可执行程序,所以学习gcc和g++是非常重要的!


正文

在实际的使用中,单纯的编译执行C和C++文件gcc和g++操作几乎相同,但是内部工作原理是截然不同的!所以在后面的讲解中会以gcc为例进行讲解,g++操作与之相同!

如果您在使用时系统报错:bash: gcc(或g++): command not found,这是因为你的Linux系统没有安装gcc或g++导致的,使用命令:

yum -y install gcc //安装gcc
yum -y install g++ //安装g++

一般Linux系统是安装了gcc的,但g++需要手动安装!


gcc/g++常用命令


我们在使用中,如果想利用对应文件生成可执行程序,命令是:

gcc [-选项] [.c文件]
g++ [-选项] [.cpp文件]

如果不加(除o)的其他选项,那么默认直接生成可执行程序,程序名为a.out,我们输入命令 ./a.out 就可以执行这个程序!


自定义可执行程序名命令-o

命令:

gcc [.c文件] -o [自定义名称] //建议写法
gcc -o [自定义名称] [.c文件] //这种写法也是可以的,但是不建议

这里需要注意的是,-o选项后面紧跟的必须是自定义名称,可以想象:-o [自定义名称] 是固定组合,不能颠倒顺序乱写

g++也可以通过-o选项生成自定义名称的可执行程序!

我们都知道源文件变成可执行程序会经过四个阶段:预处理->编译->汇编->链接库,gcc/g++编译时如果不加选项是直接执行这四步,如果我们想看其中一步执行后的文件,那么就需要对应的选项。


预处理指令-E

指令:

gcc -E [.c文件] -o [文件名].i

预处理又称预编译,预处理后的文件其后缀我们默认是 .i ,而且在这里我们如果想获得预处理后的代码文件还需要指定一个文件名,如果不使用-o指定文件名,gcc会将预处理后的代码输出到屏幕上,只有指定文件名才能保存为文件!

 

代码的预处理会将头文件展开,替换宏,删除注释并执行条件编译等操作,这样才能生成一个纯C语言的代码文本方便后面继续转换!

使用vim打开后里面都是预处理后的代码

这里我们使用-E选项是让编译器执行完预处理就停下! 


编译指令-S

指令:

gcc -S [.c文件/.i预处理文件] //默认生成的文件与编译前的文件名相同但是后缀为.s
gcc -S [.c文件/.i预处理文件] -o [文件名].s //指定生成汇编代码的文件名

这里如果我们不使用-o选项编译器会生成与被编译文件名一样的文件但后缀为.s,且这里既可以对.c代码文件编译,也可以对预处理文件.i进行编译,只不过对.c文件进行编译会先执行预处理然后再执行编译,执行完编译就停下,而预处理文件则是直接进行编译然后停止!

 

编译会对预处理代码进行语法词法并和语义分析,并进行符号汇总等,然后转换为汇编代码。

vim打开编译的文件里面是汇编代码和一些符号标记

汇编指令-c

指令:

gcc -c [.c文件/.i预处理文件/.s编译文件] //默认生成与被执行文件的文件名相同后缀为.o的二进制文件
gcc -c [.c文件/.i预处理文件/.s编译文件] -o [文件名].o

这里如果我们不使用-o选项编译器会生成与被编译文件名一样的文件但后缀为.o,且这里既可以对.c代码文件,.i代码文件和.s汇编代码文件进行汇编,只不过对.c文件进行编译会先执行预处理然和编译,然后执行汇编后就停下;.i文件会先执行编译再执行汇编就停下,.s文件则直接进行汇编然后停下!

 

汇编是将汇编代码转换为二进制汇编文件且生成符号表,这里的二进制文件通过file指令查看是elf格式,通过vim打开会显示一片乱码!

 我们如果想查看这个文件,需要通过指令:

readelf -a [二进制文件.o]

 有需要的小伙伴如果缺失这个功能可以通过yum下载!指令:

yum -y install readelf

链接指令gcc

指令:

gcc [.c/.i/.s/.o文件] //默认生成a.out可执行程序(可使用-o自定义名称)

链接是形成程序的最后一步,如果前面缺少一步就会先执行没有进行的步骤!

链接会进行合并段表,将符号表进行合并和重定位等;然后会将程序与运行所需要的各种函数和库函数链接起来(编译器默认动态链接),生成可执行程序的格式是elf,也可以通过readelf指令查看!


命令巧记口诀

指令 ESc (键盘上的退出键),文件后缀 iso (镜像文件格式)


 链接库


我们都知道,每种语言都有属于自己的库,比如C语言的stdio标准库等,C++有iostream库,当程序在在运行中调用库函数时就会通过地址去库中找这个函数进行调用!在Linux系统中,标准库在 /usr/include/ 目录下,在这个目录下,是Linux系统的各种C语言动态库,除了动态库的概念外,还有静态库的概念,我们慢慢介绍!

Linux系统下的各种库

库文件格式为:lib文件名.a/so,识别时去掉lib和后缀就是文件名!

动态库的文件格式为:lib文件名.so

静态库的文件格式为:lib文件名.a

 

在Linux系统中,指令也是可执行程序都是C语言写的依赖库!


动态库-动态链接

动态库又称共享库,动态库是动态链接的库,如果程序中有该库的函数和引用则会在调用位置留下一个链接,程序运行调用该函数时就会通过这个链接找到对应的库函数并执行!这样动态库只需要存一份代码就可以实现多个程序的调用!

 

链接就是将代码中使用的库函数与对应的库链接(将调用的库函数的文件地址拷贝到文件中)。

动态库一旦被删掉,那么所有依赖该库的程序就无法执行了;动态链接只是拷贝库的地址,执行时需要跳转到库中执行。

 

在Linux系统下gcc和g++链接是默认动态链接库的,如果我们想查看文件的链接方式,指令为:

ldd [文件/程序]  //查看程序链接库方式
打开后会显示各种链接状态且可以看到动态链接的文件后缀都是.so
通过file指令还可以查看可执行程序的链接情况!

 这也说明Linux是默认动态链接的!动态链接的好处在于编译出来的程序体积小,但是跨平台能力差!


静态库-静态链接

静态库采用静态链接的方式,静态链接与动态链接不同之处在于,动态库在调用库函数时根据地址去库中寻找并调用,而静态链接则是将调用的库函数拷贝到自己的代码文件中,相当于本地存储,后续直接执行本地代码文件,不依赖任何动态库!

如果需要静态库编译代码,指令是:

gcc [-选项] [.c文件] -static

可以在预处理-编译-汇编-链接这四个阶段选择静态链接,因为链接是最后一步!

Linux的静态库需要手动安装,指令是:

yum -y install glibc-static //下载静态库

 ldd指令显示文件没有链接任何库!file指令显示可执行程序是静态链接!

 而且静态库链接因为将库代码拷贝到了本地文件,其文件体积将会变得非常大!所以Linux中默认动态链接的方式!


动静态库对比

区别动态库静态库
库函数调用方式通过链接去库中调用在本地代码中调用
跨平台性与依赖性需要依赖库运行,跨平台性差不需要依赖库,跨平台性良好
空间占用空间占用小空间占用大
加载速度需要根据链接去找库函数然后执行直接执行本地库函数代码
优点

可以实现不同进程间的资源共享,对于函数的升级只需要替换动态库文件,不需要重新编译程序,可以控制是否加载动态库,不调用函数时就不加载

所需函数直接拷贝至程序中,运行速度快程序运行无需依赖库,便于移植和跨平台 

缺点

需要调用函数,加载速度较慢,程序运行需要依赖动态库

对于函数的升级,需要重新进行编译同一份代码可能出现重复拷贝的情况,浪费空间


其他指令


声明与定义分离的编译指令

gcc [-选项] [头文件.h] [实现代码.c] [主函数.c]

声明与定义分离,只需要将程序的所有相关文件列入gcc中,然后正常编译即可!


C99标准编译指令

gcc [-选项] [.c文件] -std=c99 //以C99标准编译代码

当我们想要C99标准语法时可以使用该指令编译!


gcc和g++的联系与区别


gcc和g++都是GNU(一个组织)的编译器。

 
1、对于.c后缀的文件,gcc把它当做是C程序;g++当做是C++程序;

2、对于.cpp后缀的文件,gcc和g++都会当做c++程序。

3、编译阶段,g++会调用gcc;

4、连接阶段,通常会用g++来完成,这是因为gcc命令不能自动和c++程序使用的库连接。


 最后

Linux编译器gcc/g++的介绍到这里就结束了,相信学完的老铁肯定想动手写两个程序吧?gcc和g++如此强大的功能来自于GNU组织的开发,让我们可以在Linux上看到代码的底层知识!本节也介绍了动态库和静态库的区别和优缺点,相信大家以后能对库进行合理利用,开发出好的作品!

本次Linux编译器gcc/g++的基本知识就介绍到这里啦,希望能够尽可能帮助到大家。

如果文章中有瑕疵,还请各位大佬细心点评和留言,我将立即修补错误,谢谢!

 🌟其他文章阅读推荐🌟

Linux基础指令-CSDN博客

Linux权限的基本知识-CSDN博客

Linux编辑器vim-CSDN博客

🌹欢迎读者多多浏览多多支持!🌹​​​​​​​


http://www.ppmy.cn/news/24073.html

相关文章

UDP协议详解

目录 前言: 再谈协议 UDP协议 比较知名的校验和 小结: 前言: UDP和TCP作为传输层非常知名的两个协议,那么将数据从应用层到传输层数据是怎样进行打包的?具体都会增加一些什么样的报头,下面内容详细介绍…

Github | 个人资料自述文件配置的不完全总结

本文简单总结配置 Github 主页上个人资料自述文件的流程和参考文件。 更新:2022 / 02 / 11 Github | 配置个人主页的信息总览方法的不完全总结创建、删除个人资料自述文件编辑个人资料自述文件参考链接创建、删除个人资料自述文件 首推自然是官方说明文档 1&#…

Python 之 NumPy 简介和创建数组

文章目录一、NumPy 简介1. 为什么要使用 NumPy2. NumPy 数据类型3. NumPy 数组属性4. NumPy 的 ndarray 对象二、numpy.array() 创建数组1. 基础理论2. 基础操作演示3. numpy.array() 参数详解三、numpy.arange() 生成区间数组四、numpy.linspace() 创建等差数列五、numpy.logs…

2023最详细的接口测试用例设计教程

一、接口测试流程 1、需求讨论 2、需求评审 3、场景设计 4、数据准备 5、测试执行 二、分析接口文档元素 1、接口名称 2、接口地址 3、支持格式 4、请求方式 5、请求参数(参数名称、类型、是否必填、参数说明等) 6、返回参数(返回…

华为OD机试 - 数组合并(Python),真题含思路

数组合并 题目 现在有多组整数数组, 需要将他们合并成一个新的数组。 合并规则, 从每个数组里按顺序取出固定长度的内容合并到新的数组中, 取完的内容会删除掉, 如果该行不足固定长度或者已经为空, 则直接取出剩余部分的内容放到新的数组中, 继续下一行。 如样例 1, 获得长度…

【大数据】Hadoop-HA-Federation-3.3.1集群高可用联邦安装部署文档(建议收藏哦)

背景概述 单 NameNode 的架构使得 HDFS 在集群扩展性和性能上都有潜在的问题,当集群大到一定程度后,NameNode 进程使用的内存可能会达到上百 G,NameNode 成为了性能的瓶颈。因而提出了 namenode 水平扩展方案-- Federation。 Federation 中…

C语言经典编程题100例(21-40)

21、练习3-2 计算符号函数的值对于任一整数n,符号函数sign(n)的定义如下:请编写程序计算该函数对任一输入整数的值。输入格式:输入在一行中给出整数n。输出格式:在一行中按照格式“sign(n) 函数值”输出该整数n对应的函数值。输入样例1:10输出样例1:sig…

CF1560D Make a Power of Two 题解

CF1560D Make a Power of Two 题解题目链接字面描述题面翻译题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示思路代码实现备注题目 链接 https://www.luogu.com.cn/problem/CF1560D 字面描述 题面翻译 给定一个整数 nnn。每次操作你可以做两件事情中的一件&am…