首页 > 系统相关 >【Linux学习】(6)编译器gcc/g++

【Linux学习】(6)编译器gcc/g++

时间:2024-10-26 23:46:57浏览次数:3  
标签:gcc ++ xx 编译 编译器 Linux 静态 可执行程序

前言

本节重点:掌握gcc/g++编译器的使用,并了解其过程,原理

一、Linux编译器-gcc/g++使用

1. gcc/g++的基本使用

  1. 在前面我们学习了vim,知道如何在Linux中编写代码。
  2. 但又是如何编译代码的?——在Linux中我们编译代码使用的是gcc/g++。
  3. gcc只能用来编译C语言,g++即可编译C也可编译C++(C++兼容C)。
  4. 虽然g++也可以编译C,但是一般C语言编译,我们比较推荐gcc。
  5. gcc/g++他们对应的选项几乎一模一样,所以这里我们学习gcc即可,g++的使用直接平替gcc的使用。
  6. gcc的基本使用:gcc xx.c ——》gcc编译C源文件,如果不加任何选项,默认直接生成可执行程序a.out(tip:代码如果有语法错误等,gcc也会有编译报错。)
  7. g++的基本使用:g++ xx.cpp ——》g++编译C/C++源文件,如果不加任何选项,默认也直接生成可执行程序a.out
  8. 可以通过 -o 选项来自定义输出文件的名称,-o后面紧跟的永远是你自定义命名的目标文件。

示例:

在这里插入图片描述

#gcc编译C源文件,如果不加任何选项,默认直接生成可执行程序a.out
gcc xx.c
#g++编译C/C++源文件,如果不加任何选项,默认也直接生成可执行程序a.out
g++ xx.cpp
#如果想自定义命名目标生成的可执行文件,使用-o选项
#-o选项用于指定编译后的输出文件名
gcc xx.c -o xx.exe(推荐写法)/ gcc -o xx.exe xx.c

2. gcc编译程序的四个阶段

文本的C源代码编译成计算机可以认识的二进制程序它需要经过如下四个阶段:

  1. 预处理(进行宏替换,去注释,头文件展开,条件编译等)
  2. 编译(生成汇编)
  3. 汇编(生成机器可识别的二进制代码)
  4. 链接(生成可执行文件或库文件)

(1)预处理阶段

  1. 预处理指令是以#号开头的代码行
  2. 预处理功能主要包括宏替换、头文件展开、条件编译、去注释等
    • 宏替换:只做简单的宏替换,不做类型检查,因为在编译阶段才会做语法检查
    • 头文件展开:把头文件的内容拷贝到源文件的行为叫做头文件展开
    • 条件编译:条件编译可以在编译前对代码进行条件判断和处理,从而实现代码的选择性编译
    • 去注释:注释是帮助我们理解代码的,计算机并不需要,所以在预处理阶段就将注释去掉
  3. 预处理阶段指令格式:gcc -E xx.c -o xx.i
  4. -E选项:告诉gcc,从现在开始进行程序的翻译,将预处理工作做完就停下来,不要往后走了!
  5. -o xx.i:预处理之后产生的结果都放在xx.i文件中(tip:不带-o选项,默认把输出结果打印到显示器上)
  6. “.i”文件为已经预处理的C原始程序,还是C语言。

示例:

在这里插入图片描述
加油站:

  1. 我们为什么可以在Windows或者Linux上进行C/C++或者其他形式的开发?

    答:我们的系统一定提前或者后续安装了,C/C++开发相关的头文件、库文件。编译型语言,安装开发包,必定是下载安装对应的头文件+库文件。
  2. 在Linux中开发相关的头文件,一般在/usr/include/这个目录下。在这里插入图片描述
  3. /usr/include/这个目录是我们Linux系统中gcc/g++编译器,默认搜索头文件时的搜索路径。
  4. 条件编译:条件编译可以在编译前对代码进行条件判断和处理,从而实现代码的选择性编译
    • #ifdef:如果定义了某个宏,则编译随后的代码块
    • #else:如果前面的#ifdef条件不满足,则编译#else后面的代码块
    • #endif:结束条件编译块,每一个完整的条件编译语句都需要#endif来收尾
    • 使用场景:特性开关——根据是否定义了某个宏来启用或禁用某些功能在这里插入图片描述
  5. 条件编译的宏通常在编译命令中通过 -D 选项定义,格式:gcc xx.c -D宏名字在这里插入图片描述
gcc -E xx.c -o xx.i -DDEBUG
# -E:告诉gcc,从现在开始进行程序的翻译,将预处理工作做完就停下来,不要往后走了
# -o xx.i:把预处理结果放到xx.i文件中
# -D:在gcc命令行中可通过选项-D定义宏

(2)编译阶段

  1. 在编译阶段,gcc首先要检查代码的规范性、是否有语法错误等,在检查无误后,gcc把代码翻译成汇编语言
  2. 实例:gcc -S mycode.i -o mycode.s
  3. -S选项:从现在开始进行程序的翻译,将编译工作做完,就停下来在这里插入图片描述

(3)汇编阶段

  1. 汇编阶段是把编译阶段生成的“.s”文件转成目标文件
  2. 实例:gcc -c mycode.s -o mycode.o
  3. -c选项:从现在开始进行程序的翻译,将汇编工作做完,就停下来
  4. mycode.o:可重定位目标二进制文件,简称目标文件,.obj文件不可以独立执行,虽然已经是二进制了,但需要经过链接(库)才能执行在这里插入图片描述
  5. 注意vim是文本编辑器,所以打开的二进制文件显示是乱码!
  6. 二进制查看工具:od 二进制文件

(4)连接

  1. 在成功编译之后,就进入连接阶段
  2. 连接:将可重定位目标二进制文件和库进行连接形成可执行程序
  3. 实例:gcc mycode.o -o mycode.exe在这里插入图片描述

问题:程序翻译的四个过程中,连接没有选项,但是预处理、编译、汇编都有选项,我们有什么技巧可以帮助我们记忆吗?

答案是,有的,如下:

  • 预处理、编译、汇编的选项按照顺序,是ESc,我们可以看键盘的左上角【ESC】键帮助我们记忆
  • 预处理、编译、汇编文件的后缀顺序,是iso,镜像文件的后缀就是iso,所以我们可以通过镜像文件后缀帮助我们记忆
  • 虽然Linux系统中文件后缀没有意义,但是不代表Linux系统上运行的软件不需要后缀,就比如这里的gcc,所以建议我们一般带上正确的后缀

(5)重要概念:函数库

  • 重新理解函数,是个函数他就第一有定义,第二有声明,第三有调用
  • 在C语言中我们常常调用库函数来编程,但是在我们的程序中,并没有定义库函数的实现,且在预编译中包含的头文件中也只有该函数的声明,而没有定义库函数的实现,那么,是在哪里实现库函数的呢?
  • 答案是:库,库中给我们提供方法的实现
  • 因为我们用的是C语言,所以我们使用的就是C语言标准库,其本质是一个文件,有路径(C标准库在Linux系统中默认在/usr/lib64/libc.so*)在这里插入图片描述
  • 在不同的平台下库有不同的格式:
    • 在Linux下,以.so为后缀的是动态库,以.a为后缀的是静态库
    • 在Windows下,以.dll为后缀的是动态库,以.lib为后缀的是静态库
  • 库有自己的命名规则,以lib为前缀,以.so.xxx为后缀(xxx是版本号):libname.so.xxx
  • 在Linux系统中只会默认帮我们安装动态库,静态库默认是没有安装的


归纳标准库:

  1. 方法的实现就是在库中
  2. 库其实就是把源文件(.c),经过一定的翻译,然后打包——只给你提供一个文件即可,不用给你提供太多的源文件,也可以达到隐藏文件的目的
  3. 头文件提供方法的声明,库文件提供方法的实现(不让我们做重复工作,站在巨人的肩膀)+你的代码 = 你的软件

(6)函数库一般分为静态库和动态库两种

  • 静态库:静态库是指编译连接时,把库文件的代码全部拷贝到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”
  • 动态库:动态库与之相反,在编译链接时并没有把库文件的代码拷贝到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”。
  • 帮助理解:
    • 动态库我们可以将其比作网吧,当我们想玩游戏的时候,就可以去网吧玩。(动态库即共享库,所以我们的网吧只有一个)但是当有一天网吧被查封了,我们就不能玩了(动态库不能缺失,一但对应的动态库缺失,影响的不止一个程序,可能导致很多程序都无法进行正常运行!)
    • 静态库我们将其比作电脑销售商,我们买了电脑之后,没有网吧和电脑销售商倒闭了,也可以有自己的电脑可以玩游戏(静态库在链接的时候,会把自己拷贝到目标程序中,所以该程序可以独立运行)
  • 库有动态库和静态库两种,自然我们的.o和库的链接方式也有两种:
    • 动态链接:在Linux中,编译形成可执行程序,默认采用的就是动态链接——提供动态库
    • 静态链接:在Linux中,如果要按照静态链接的方式,形成可执行程序,需要添加-static选项——
      提供静态库
    • 注意:动态链接只能使用动态库,静态链接只能使用静态库
    • 验证:ldd指令可查看一个可执行程序所依赖的动态库在这里插入图片描述
    • 补充:file指令也可查看一个可执行程序使用了什么链接方式在这里插入图片描述
  • 我们的Linux系统一般只默认安装了动态库,静态库需要我们自己安装
  • 如果我们没有静态库,就不能-static静态链接在这里插入图片描述
  • 安装静态库:
sudo yum install -y glibc-static # 安装C静态库
sudo yum install -y libstdc++-static # 安装C++静态库
  • 如果我们没有动态库,只有静态库,gcc不带-static选项能找到静态库吗——能,gcc只是默认优先动态链接
  • -static的本质:改变链接的优先级,所有的链接要求全部变成静态链接(注:只适配一次)
  • 连接不一定是纯的全部动态链接或静态链接,混合的。
  • 动态库VS静态库:
    • 动态库因为是共享库,有效的节省资源(磁盘空间、内存空间、网络空间等)【优点】,动态库一但缺失,导致各个程序都无法运行【缺点】
    • 静态库,不依赖库,程序可以独立运行【优点】,但体积大、比较消耗资源【缺点】

(7)debug和release

  1. 源文件编译时有有两种模式,debug模式和release模式
  2. debug通常称为调试版本,它包含调试信息,并且不做任何优化,便于程序员调试程序(因为debug模式在形成可执行程序的时候,添加了debug信息,所以可以被追踪调试)
  3. release称为发布版本,它往往是进行了各种优化,是程序在代码大小和运行速度上都是最优的,以便用户很好的使用
  4. 在Linux系统上gcc形成的可执行程序,默认是release版本
  5. 如果想以debug形式编译,我们需要添加-g选项(-g生成调试信息)
  6. 验证:readelf -S指令可以读取可执行程序对应的二进制构成,再通过grep debug可以将debug信息过滤出来在这里插入图片描述

二、执行可执行程序

  • 为什么我们运行自己的可执行程序的时候,要在前面带./呢?

因为执行一个程序,你就必须要先找到这个程序

而要找到这个程序,其本质就是要定位这个程序的二进制文件在系统中的位置

./就是告诉我们的系统,当前我要执行的可执行程序在我的当前路径下

  • 重新理解路径

为什么要有路径——答案是:因为要定位文件

  • 系统命令也是一个可执行的二进制文件,为什么执行的时候就不用带./呢?

不带./并不代表不用找到,因为系统会默认帮我们找到(通过环境变量)

  • 总结:
  1. 在Linux系统中执行任何一个程序,都必须要找到这个程序对应的二进制文件
  2. 系统命令因为系统帮我们配了环境变量,系统自己找得到,所以系统命令不用带路径,直接就可以执行
  3. 而我们自己的程序,系统找不到,所以需要我们自己指定路径./,才能执行

标签:gcc,++,xx,编译,编译器,Linux,静态,可执行程序
From: https://blog.csdn.net/wangjiushun/article/details/141369479

相关文章

  • 7.C++数组
    C++数组1.数组1.数组在C++中,数组是一种存储固定大小的相同类型元素的序列。数组的所有元素都存储在连续的内存位置上。这种数据结构非常适合于存储具有固定数量和相同数据类型的元素集合。声明数组声明数组的基本语法如下数据类型数组名[数组大小];声明一......
  • 每日OJ题_牛客_城市群数量_FloodFill_C++_Java
    目录牛客_城市群数量_BFS/并查集题目解析C++代码Java代码牛客_城市群数量_BFS/并查集城市群数量_牛客题霸_牛客网(nowcoder.com)描述:        给定一个n个节点的邻接矩阵m。节点定义为城市,如果a城市与b城市相连,b与c城市相连,尽管a与c并不直接......
  • 【Linux探索学习】第八弹——Linux工具篇(三):Linux 中的编译器 GCC 的编译原理和使用详
    #1024程序员节|征文#Linux下的vim编辑器:【Linux探索学习】第七弹——Linux的工具(二):Linux下vim编辑器的使用详解-CSDN博客前言:在上一篇我们学习了如何在Linux环境下直接用vim编辑器来进行编辑代码,今天我们来学习如何运行我们所编辑的代码,运行代码就需要编译器,也就是我们下......
  • C++/CLI使用Office.Interop库创建excel,同时解决写入速度慢的问题
    boolWriteExcelFile_OfficeInterop(String^path,DataSet^dt, conststd::vector<std::string>&sheetName,boolhideColumnName) { //Ifthefilealreadyexists,deleteitandthengeneratefile if(System::IO::File::Exists(path)) { try......
  • C++/CLI 类内多线程编程 多个参数
    usingnamespaceSystem;usingnamespaceSystem::Threading;namespaceNNNN{ publicrefclassTTTT { staticThread^t1; staticThread^t2; voidfun() { t1=gcnewThread(gcnewParameterizedThreadStart(DoWork1)); t2=gcnewThread(gcnewP......
  • 最新毕设-SpringBoot-健康体检系统-12791(免费领项目)可做计算机毕业设计JAVA、PHP、爬
    springboot健康体检系统摘要在如今IT技术快速发展和Internet广泛应用的时代,电子和网络技术给人们生活带来了便利,同时也会直接或间接损害人们的健康。所以,本次的毕业设计创作的意义就是通过信息化的统一管理,给用户尽心健康体检预约提供了方便。本设计主要实现集人性化、高效......
  • (2024最新毕设合集)基于Django的房价分析平台-65434|可做计算机毕业设计JAVA、PHP、爬虫
    摘要本论文主要论述了如何基于Django框架开发一个房价分析平台,本系统将严格按照软件开发流程进行各个阶段的工作,通过爬虫技术对贵州省的房价数据进行爬取,面向对象编程思想进行项目开发。在引言中,作者将论述房价分析平台的当前背景以及系统开发的目的,后续章节将严格按照软件......
  • 【信奥赛·算法基础】CSP-J C++ 贪心算法示例汇总
    序言为了更清晰的了解贪心算法,我把常见的贪心算法示例做了一个总结,把问题和策略,以及代码示例放到了一起,方便学习和分析,这里示例仅以C++为例,其他语言可根据示例调整即可一、钱币找零问题问题描述:给定不同面额的钱币以及每种面额的数量,用最少的钱币张数凑齐给定的总金额。......
  • C++ (4) 面向对象编程,C++的魔法生物养成记
    面向对象编程:C++的魔法生物养成记在C++的世界里,面向对象编程(OOP)就像是魔法生物的养成游戏。你将扮演一名魔法师,通过编写代码来创造和培养自己的魔法生物。这些生物拥有自己的属性(数据)和能力(函数),它们可以在你的程序世界中自由行动和互动。现在,让我们拿起魔杖(键盘),开始这场魔......
  • C++ (7) 内存管理:掌握魔法能量的流动
    内存管理:掌握魔法能量的流动在C++的魔法世界中,内存管理是一项至关重要的技能。它涉及到程序如何分配和释放内存资源。如果处理不当,可能会导致程序崩溃或内存泄漏,就像一个魔法师失去了对魔法能量的控制。现在,让我们学习如何成为一个负责任的魔法师,掌握内存管理的艺术。6.......