当前位置: 首页 > news >正文

gcc/g++使用

gcc概述

gcc 是 GNU Compiler Collection(GNU 编译器集合)的缩写。它是一套用于编译多种编程语言的编译器,包括 C、C++、Objective-C、Fortran 等。
gcc 具有以下一些重要特点和优势:

  1. 高度的可移植性:可以在多种操作系统和硬件平台上运行。
  2. 强大的优化能力:能够生成高效的机器代码。
  3. 丰富的编译选项:允许开发者对编译过程进行精细的控制。
    gcc是GCC中的C语言编译器,而g++是GCC中的C++编译器。这里讲解gcc的使用,而g++的语法和选项与gcc都是一致的。
    gcc编译C语言的基本步骤:
  4. 先使用vim指令,在插入模式下写一段C语言代码,写完保存文件并退出
  5. 编译C语言。语法:gcc 文件名
  6. 运行代码。./a.out,如果不加任何信息,默认情况下生成的可执行文件名是a.out。如果你想要编译的同时,指定编译后生成文件的名称,就加上-o选项,后紧跟着编译后文件的名称:gcc 文件名 -o 指定文件名
    下面我将演示一下这个过程:
    请添加图片描述

编译C语言要经过4个过程,由编译链接两个⼤的过程组成的,⽽编译⼜可以分解成:预处理(有些书也叫预编译)、编译、汇编三个过程。
请添加图片描述

⼀个C语⾔的项⽬中可能有多个 .c ⽂件⼀起构建,那多个 .c ⽂件如何⽣成可执⾏程序呢?

  • 多个.c⽂件单独经过编译出编译处理⽣产对应的⽬标⽂件。
  • 注:在Windows环境下的⽬标⽂件的后缀是 .obj ,Linux环境下⽬标⽂件的后缀是 .o
  • 多个⽬标⽂件和链接库⼀起经过链接器处理⽣成最终的可执⾏程序。
  • 链接库是指运⾏时库(它是⽀持程序运⾏的基本函数集合)或者第三⽅库。
    如果再把编译器展开成3个过程,那就变成了下⾯的过程:
    请添加图片描述

对C语言翻译的过程有了一定了解后,我们来深入学习这4个过程

预处理

预处理过程主要包含4个工作:去注释、头文件展开、条件编译和宏替换。
预处理指令是以#号开头的代码行。语法:gcc –E test.c –o test.i。这条指令的意思是告诉gcc,从现在开始进行程序的翻译,不要往后走了。

  • 选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。
  • 选项“-o”是指目标文件,“.i”文件为已经过预处理的C原始程序
    这里我们经过处理后的文件改成test.i,一般而言经过预处理的文件都以.i为后缀
    请添加图片描述

左侧是test.i文件,右侧是源文件。
我们可以看到,经过预处理后,左侧的代码变成了800多行,并把注释删除了,也完成了宏替换。而这800多行代码就是头文件<stdio.h>的展开
总结预处理的处理规则:

  • 将所有的 #define 删除,并展开所有的宏定义。
  • 处理所有的条件编译指令,如: #if、#ifdef、#elif、#else、#endif 。
  • 处理#include 预编译指令,将包含的头⽂件的内容插⼊到该预编译指令的位置。这个过程是递归进⾏的,也就是说被包含的头⽂件也可能包含其他⽂件。
  • 删除所有的注释
  • 添加⾏号和⽂件名标识,⽅便后续编译器⽣成调试信息等。
  • 或保留所有的#pragma的编译器指令,编译器后续会使⽤。

编译(生成汇编)

编译过程就是将预处理后的⽂件进⾏⼀系列的:词法分析、语法分析、语义分析及优化,⽣成相应的汇编代码⽂件。

  • 在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。
  • 用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
    编译过程指令:gcc -S test.i -o test.s。该阶段的文件,一般以.s结尾。
    请添加图片描述

这时已经变成汇编语言的文件了。

汇编(生成机器可识别代码)

汇编阶段是把编译阶段生成的“.s”文件转成目标文件。在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了
指令:gcc -c test.s -o test.o
生成的文件test.o表示的是可重定位目标二进制文件,简称目标文件(.o),这时虽然已经是二进制了,但是需要经过链接才能执行。一般而言,该阶段的文件以.o或者.obj结尾。在Linux中习惯用.o,在Windows中习惯用.obj
请添加图片描述
可以看到全是乱码,原因是我用vim编辑器打开的,它不能解析二进制文本文件,所以会出现乱码。

链接(生成可执行文件或库文件)

语法:gcc test.o –o hello
在成功编译之后,就进入了链接阶段。在链接过程中,GCC 会自动处理一些常见的系统标准启动文件和库文件。例如,crt1.ocrti.ocrtbegin.ocrtend.ocrtn.o 是 GCC 加入的系统标准启动文件,对于一般应用程序,这些启动文件是必需的。而 -lc 选项用于链接标准 C 库(libc),因为像 printf 等函数就是在标准 C 库中实现的。如果使用 -nostdlib 选项(常用于裸机/bootloader、Linux 内核等程序),则不会链接系统标准启动文件和标准库文件,链接会失败,因为这些程序通常不需要完整的标准库和启动文件。

函数库

我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用。
Linux中C语言标准库:
请添加图片描述
其中.so表示的是动态库,.a表示的是静态库,命名规则是libname.so.xxx,后面的.xxx是版本号。
我们可以通过ldd指令来查看一个可执行文件链接了那些库:ldd test.exe。Windows中文件的类型常用后缀来看,而在Linux中文件后缀基本没用,这里起的名字只是因为我们经常用Windows系统,已经对Windows很熟悉了,这样比较容易分辨文件类型。
请添加图片描述
图片中指明了一些库在系统中的路径。也就是说我们的很多头文件,都已经早早地在Linux中下载好了,因此我们可以在Linux上运行C语言代码。我们可以在Linux系统中查看有哪些头文件。
请添加图片描述
函数库一般分为2种:动态库和静态库
动态库(共享库)

  • 特点:
    • 代码共享:多个程序可以同时使用同一个动态库,节省内存空间。
    • 灵活性:动态库的更新不要求重新编译使用它的程序。
    • 较小的可执行文件:因为可执行文件不需要包含库的代码,所以生成的可执行文件相对较小。
  • 示例:例如,libc.so 是标准 C 库的动态库。
  • 加载方式:程序在运行时动态加载所需的动态库。
  • 缺点:动态库一旦缺失,会导致各个程序都无法运行。
    静态库
  • 特点:
    • 独立性:静态库被链接到可执行文件中,生成的可执行文件不依赖外部的库文件。
    • 稳定性:不受动态库版本更新和位置变化的影响。
  • 示例:比如,libc.a 是标准 C 库的静态库。
  • 加载方式:在程序编译时,库的代码被直接复制到可执行文件中。
  • 缺点较大的可执行文件:由于包含了库的代码,可执行文件可能较大。
    如果使用动态库,那么在不同的机器上运行时,只要系统中有正确的动态库并且路径设置正确,程序就能运行;而如果使用静态库,生成的可执行文件可以在任何兼容的系统上直接运行,无需担心库的存在与否,但可执行文件的大小可能会明显增大。gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证。
    通过动态库实现的链接,叫做动态链接,通过静态库实现的链接叫做静态链接
    如果我们想生成使用静态链接的方式生成一个文件,那么需要加上-static的选项。如果没有安装过静态库的小伙伴们,是无法执行该指令的,这是因为Linux默认不安装静态库,需要手动安装,现在我们先来按照一下。
    指令:yum install -y glibc-static libstdc++-static。该指令需要root权限,要么sudo,要么以root身份执行。C语言的静态库是glibc-static。安装好之后看下面这张图片
    请添加图片描述

我们发现使用静态链接的方式,文件大小变得很大,因为静态库会把库的代码直接复制到可执行文件当中。使用ldd指令:
请添加图片描述
告诉你这不是一个动态链接的可执行文件。

相关文章:

  • agasa文件传输:内网文件互传的高效解决方案
  • 【Kubernetes基础】--查阅笔记1
  • Missashe考研日记-day20
  • svn 分支(branch)和标签(tag)管理
  • Cherry Studio + MCP,从0到1保姆教程,3个场景体验
  • GIT的一些操作
  • SomeIP:服务端or客户端发送event或method源码参考via CAPL
  • Java使用ANTLR4对Lua脚本语法校验
  • [c语言日寄]时间复杂度
  • 密码太多记不住?用Trae开发一个密码管理插件
  • linux电源管理(二),内核的CPUFreq(DVFS)和ARM的SCPI
  • OSI参考模型
  • 路由交换网络专题 | 第三章 | BGP | 选路原则 | router-id选举 | BGP网段宣告方式 | 抑制路由
  • DeepSeek 接入 Excel 完整教程
  • mysql 数据库localhost密码忘记
  • 主流程序员接单平台的分类整理与分析
  • android​​弱网环境数据丢失解决方案(3万字长文)
  • MyBatis-plus笔记 (上)
  • 深度学习中的数值稳定性处理详解:以SimCLR损失为例
  • 火山引擎旗下的产品
  • 全国空港口岸居首,浦东机场口岸今年出入境已突破1000万人次
  • 岳阳一安置房天花板被指用手能抠出洞,住建局已带第三方机构去检测
  • 昆明一企业家向母校捐赠多媒体教室
  • 月薪3500元保安跳槽被索20万违约金,仲裁裁决:不支持
  • 中华人民共和国和越南社会主义共和国关于持续深化全面战略合作伙伴关系、加快构建具有战略意义的中越命运共同体的联合声明