【Linux】编译器gcc g++和调试器gdb的使用

news2024/7/4 6:05:15

文章目录

  • 1.编译器gcc/g++
    • 1.1C语言程序的翻译过程
      • 1.预处理
      • 2.编译
      • 3.汇编
      • 4. 链接
    • 1.2 链接方式与函数库
      • 1.动态链接与静态链接
      • 2.动态库与静态库
    • 1.3 gcc与g++的使用
  • 2.调试器gdb
    • 2.1debug和release
    • 2.2gdb的安装
    • 2.3gdb的使用
    • 2.4gdb的常用指令
  • 3.总结

1.编译器gcc/g++

1.1C语言程序的翻译过程

1.预处理

在此阶段做的事情:

  1. 头文件展开:把我们编写的代码中的包含头文件的代码替换成头文件本身
  2. 删除所有的注释
  3. #define定义的符号和宏全部替换
  4. 执行条件编译

在Linux下,我们可以通过指令让gcc只执行预处理操作

gcc -E test.c -o test.i
# -E 表示从现在开始,进行程序的翻译,当预处理结束时停下来
# -o 表示指明产生的文件的名称

image-20230227224027072

可以看到经过预编译之后,#include包含头文件的代码没有了,但是文件中多了几百行,这些多的就是头文件的内容被拷贝进来了,注释部分被删除,宏定义被替换了,条件编译也转变成了执行过后的结果。

2.编译

此阶段做的事情:

  • 语法分析
  • 词法分析
  • 语义分析
  • 符号汇总

最终的结果就是把C语言代码变成汇编语言代码

在Linux下需要执行的指令是:

gcc -S test.i -o test.s
# -S 表示从现在开始,执行程序的翻译,做完编译工作之后,变成汇编代码就停下来
# 变成的汇编代码的后缀名是.s

image-20230227221608377

打开test.s之后我们可以发现,里面的代码意见已经变成汇编指令了。

3.汇编

此阶段做的事情:把汇编代码变成二进制(这里的二进制不是可执行的,叫做二进制目标文件)

在Linux下需要执行的指令是:

gcc -c test.s -o test.o
# -c 表示从现在开始,进行程序的翻译,做完汇编工作,变成可重定向的目标二进制,就停下来
# 重定向的目标二进制文件的后缀名是.o

image-20230227222129347

可以看到,此时文件内已经变成了我们看不懂的二进制代码,当他以二进制的形式打开时,是这样的

image-20230227222425180

4. 链接

此阶段做的事情:把本地编写的代码和c标准库中的代码合并,形成可执行的二进制文件

  • 合并段表:编译器会把在汇编阶段生成的多个目标文件中相同格式的数据合并在一起,最终形成一个 .exe 文件。
  • 符号表的合并和重定位:符号表的合并是指编译器会把在汇编阶段生成的多个符号表合并为一个符号表;重定位则是指当同一个符号出现在两个符号表中时,编译器会选取其中和有效地址相关的那一个,舍弃另外一个

在Linux下需要执行的指令是:

gcc test.o -o mytest
# 链接阶段是程序翻译的最后一个阶段,不需要加任何选项
# 默认生成的可执行文件的文件名是a.out,我们可以通过-o选项指定

image-20230227223200457

可以看到,产生的文件mytest就是可执行的程序。

注:

  1. 对于上述的几个Linux下gcc指令的选项和产生文件的后缀名,这里有一个方便记忆的小技巧,预编译、编译、链接的选项分别是ESc,对应着键盘左上角的按键Esc,产生的文件后缀名是iso,对应着光盘映像文件的后缀名
  2. 上述的分段执行只是为了方便我们能够更加细致的看到程序翻译的过程,在实际使用gcc的时候,只需要使用指令gcc 原文件名 -o 产生的可执行文件名或者gcc 原文件名即可。

1.2 链接方式与函数库

1.动态链接与静态链接

我们在写代码的过程中,会经常用到库函数,类似printf,scanf,strlen等函数,这些函数在我们的代码中只是调用了它们,并没有实现,那么是谁实现的呢?答案是库函数,是别人预先写好的

同时,程序在预处理、编译和汇编阶段处理的都是我们自己编写的代码,只有在链接的时候,库函数的实现才会和我们的代码关联起来 (符号表的重定位);所以,链接的本质是我们在调用库函数时如何与标准库相关联的问题

程序的链接方式一共有两种:动态链接与静态链接

  • 动态链接是指执行代码时,如果遇到库函数调用就跳转到动态库中对应函数的定义处,然后执行该函数,执行完毕后再跳转回原程序并继续往下执行;它的优点是形成的可执行程序小,缺点是受到动态库变动 (删除、升级等) 的影响

  • 静态链接则是直接将本程序内部要使用的库函数从对应的静态库中拷贝一份过来;它的优点是不与静态库产生关联,即不受静态库变动 (删除、升级等) 的影响;缺点是形成的可执行程序非常大。

2.动态库与静态库

函数库是一些事先写好的,用于给别人复用的函数的集合,函数库一般分为静态库和动态库两种

静态库是指在编译链接时,把包含的库文件全部拷贝到可执行文件中,然后在运行时就不再需要库文件了,但是由于拷贝了全部内容,所以生成的文件会很大。静态库在Linux下的后缀名是.a,在Windows下后缀名是.lib

动态库:也叫共享库,与静态库相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为.so,在Windows下后缀名是.dll

那么,我们验证一下,我们在Linux下编译是怎么链接的

验证方法:使用file指令可以看到调用的库是动态库还是静态库

image-20230301000408464

我们可以看到,gcc默认的链接方式是动态链接,那么怎么让它使用静态链接呢?只需要在编译指令后面加上-static

image-20230301000647594

这个时候,我们查看一下两个文件的详细信息

image-20230301000746827

可以看到,使用静态链接产生的可执行文件,大小比动态链接产生的文件大得多。

这里补充一点非常重要的事情:一定不要删除系统中的C动态库,因为Linux系统中的基本上所有指令都是使用C语言写的,如果没有C动态库,会导致很多指令都无法使用,最终的解决方案只能是重装系统。

1.3 gcc与g++的使用

gcc 选项

  • -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
  • -S 编译到汇编语言不进行汇编和链接
  • -c 编译到目标代码
  • -o 文件输出到 文件
  • -static 此选项对生成的文件采用静态链接
  • -g 生成调试信息。GNU 调试器可利用该信息。
  • -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
  • -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
  • -w 不生成任何警告信息。
  • -Wall 生成所有警告信息

我们在使用静态链接的方式编译的时候,可能会发现报错,因为有部分Linux机器没有安装C静态库,所以需要我们手动安装

# 手动安装C静态库
sudo yum install -y glibc-static

同时,部分Linux机器也是没有安装g++的,也需要我们手动安装

# 安装g++
sudo yum install -y gcc-c++
# 安装c++静态库
sudo yum install -y libstdc++-static

2.调试器gdb

2.1debug和release

debug和release是程序编译的类型版本,debug是调试版本,其中包含了程序的调试信息,release是程序的发布版本,其中没有调试信息,并且进行了部分优化(例如对死循环的优化)。

image-20230307154214967

可以看到,debug版本比release版本要大一点,其中多的内容就是调试信息,所以gdb调试必须要在debug模式下调试,如果是release版本下不可执行调试

image-20230307154708380

会显示no debugging symbols found(没有找到调试标志)

Linux下gcc/g++编译出来的程序默认是release版本

到这里我们总结一下之前所学到的关于Linux下的一些默认行为

  1. gcc/g++的默认行为

    • 默认连接方式是动态连接(静态链接需要加-static)

    • 默认编译版本是release(编译debug版本需要加-g)

  2. vim的默认行为

    • 打开后的默认模式是命令模式

2.2gdb的安装

sudo yum install -y gdb

image-20230307155740754

2.3gdb的使用

  • 第一步:使用-g指令编译源代码,产生debug版本的可执行程序

    image-20230307160313457

  • 第二步:执行gdb FileName进入调试

    image-20230307160551534

  • 第三步:输入调试指令进行调试即可

  • 第四步:ctrl+d或者q/quit退出调试

2.4gdb的常用指令

  • list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
  • list/l 函数名:列出某个函数的源代码。
  • r或run:运行程序。
  • n 或 next:单条执行。
  • s或step:进入函数调用
  • break(b) 行号:在某一行设置断点
  • break 函数名:在某个函数开头设置断点
  • info break :查看断点信息。
  • finish:执行到当前函数返回,然后挺下来等待命令
  • print§:打印表达式的值,通过表达式可以修改变量的值或者调用函数
  • p 变量:打印变量值。
  • set var:修改变量的值
  • continue(或c):从当前位置开始连续而非单步执行程序
  • run(或r):从开始连续而非单步执行程序
  • delete breakpoints:删除所有断点
  • delete breakpoints n:删除序号为n的断点
  • disable breakpoints:禁用断点
  • enable breakpoints:启用断点
  • info(或i) breakpoints:参看当前设置了哪些断点
  • display 变量名:跟踪查看一个变量,每次停下来都显示它的值
  • undisplay:取消对先前设置的那些变量的跟踪
  • until X行号:跳至X行
  • breaktrace(或bt):查看各级函数调用及参数
  • info(i) locals:查看当前栈帧局部变量的值
  • quit:退出gdb

下面会使用实例来演示部分指令

image-20230307161817138

  • list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。

    image-20230307162703430

  • list/l 函数名:列出某个函数的源代码

    image-20230307162759348

  • break(b) 行号:在某一行设置断点

  • info break :查看断点信息

  • break 函数名:在某个函数开头设置断点

    image-20230307163145936

  • d + 断点编号:删除断点

  • delete breakpoints:删除所有断点

    image-20230307164814166

  • r或run:运行程序

    image-20230307163331719

  • s或step:进入函数调用

    image-20230307163447046

    进入了AddToVal函数内部,遇到断点停下

  • n 或 next:单条执行

    image-20230307163634833

    每个n执行一行

  • print§:打印表达式的值,通过表达式可以修改变量的值或者调用函数

    image-20230307163850478

  • display 变量名:跟踪查看一个变量,每次停下来都显示它的值

    image-20230307164008886

  • undisplay:取消对先前设置的那些变量的跟踪

    image-20230307164201532

  • finish/fin:执行到当前函数返回,然后停下来等待命令

    image-20230307164252125

3.总结

到此,我们学习了Linux下的基本指令与操作,权限相关的概念,学习了yum工具,能够在Linux下进行软件的安装,学习了vim的使用,能够在Linux下写代码,学习了gcc/g++的使用,能够在Linux下编译代码,学习了gdb的使用,能够在Linux下调试代码,学习了make/makefile,能够在Linux下使用多文件编程,为我们在Linux下编程提供了便利,编写了我们的第一条Linux程序----进度条,学会了使用git命令行,能够把Linux下的代码上传到Gitee/Github上。Linux的工具篇到此结束,下面,我们将会遇到Linux的第一座大山----进程。

抬头是山,路在脚下,与诸君共勉

本章完

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

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

相关文章

一个ELF文件分析和逆向的过程

CrackMe1、2分析和逆向的过程 1. CrackMe1、2相关信息 CrackMe1 1、CrackMe1是一个ELF可执行文件,可在Android中独立执行 2、通过adb(Android SDK中默认带有adb工具)将CrackMe1 push到远程Android设备中,建议放在/data/local/tmp目录下 3、打开adb shel…

关于MSVCR100.dll、MSVCR100d.dll、Msvcp100.dll、abort()R6010等故障模块排查及解决方法

一、常见故障介绍  最近在开发相机项目(项目细节由于公司保密就不介绍了),程序运行5个来月以来首次出现msvcr100.dll故障等问题,于是乎开始了分析之路,按照度娘上的一顿操作,期间也是出现了各种不一样的问…

Lombok常见用法总结

目录一、下载和安装二、常见注释(一)Data(二)Getter和Setter(三)NonNull和NotNull(不常用)(四)ToString(不常用)(五&#…

一款丧心病狂的API测试工具:Apifox!

你好,我是测试开发工程师——凡哥。欢迎和我交流测试领域相关问题(测试入门、技术、python交流都可以) 我们平时在做接口测试的时候,对于一些常用的接口测试工具的使用应该都非常熟悉了: 接口文档:Swagge…

Databend 开源周报 第 83 期

Databend 是一款现代云数仓。专为弹性和高效设计,为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务:https://app.databend.com 。Whats New探索 Databend 本周新进展,遇到更贴近你心意的 Databend 。Support for WebHDFSHDFS 是大数…

ArrayList集合底层原理

ArrayList集合底层原理ArrayList集合底层原理1.介绍2.底层实现3.构造方法3.1集合的属性4.扩容机制5.其他方法6.总结ArrayList集合底层原理 1.介绍 ​ ArrayList是List接口的可变数组的实现。实现了所有可选列表操作,并允许包括 null 在 内的所有元素。 每个 Array…

静态库和动态库的打包与使用

静态库和动态库 静态库和动态库的打包 生成可执行程序时链接使用 运行可执行程序时加载使用 提前声明,笔者示例的文件有mian.c/child.c/child.h。OK,我们先了解一下,库文件是什么?它其实就是打包了一堆实现常用功能的代码文件. ⭐…

Python之re库用法细讲

文章目录前言一、使用 re 模块的前期准备工作二、使用 re 模块匹配字符串1. 使用 match() 方法进行匹配2. 使用 search() 方法进行匹配3. 使用 findall() 方法进行匹配三、使用 re 模块替换字符串四、使用 re 模块分割字符串总结前言 在之前的博客中我们学习了【正则表达式】的…

C++ typedef用法详解

typedef的4种常见用法:给已定义的变量类型起个别名定义函数指针类型定义数组指针类型为复杂的声明定义一个新的简单的别名总结一句话:“加不加typedef,类型是一样的",这句话可以这样理解:没加typedef之前如果是个…

云原生架构设计原则及典型技术

云原生是面向云应用设计的一种思想理念,充分发挥云效能的最佳实践路径,帮助企业构建弹性可靠、松耦合、易管理可观测的应用系统,提升交付效率,降低运维复杂度。代表技术包括不可变基础设施、服务网格、声明式 API 及 Serverless 等…

Apk加固后多渠道打包

之前一直使用360加固宝进行apk的加固打包,可以一键加固并打多渠道打包。但是,现在360加固宝收费了,在进行加固,多渠道打包,就得一步一步自己操作了,会很繁琐。所以,本文使用 360加固美团Wallet …

c++11 标准模板(STL)(std::unordered_map)(五)

定义于头文件 <unordered_map> template< class Key, class T, class Hash std::hash<Key>, class KeyEqual std::equal_to<Key>, class Allocator std::allocator< std::pair<const Key, T> > > class unordered…

Java开发 - 单点登录初体验(Spring Security + JWT)

目录​​​​​​​ 前言 为什么要登录 登录的种类 Cookie-Session Cookie-Session-local storage JWT令牌 几种登陆总结 用户身份认证与授权 创建工程 添加依赖 启动项目 Bcrypt算法的工具 创建VO模型类 创建接口文件 创建XML文件 补充配置 添加依赖 添加配…

凭一部手机,7天赚20万?会剪辑的人有多吃香!

影视剪辑容易遇到哪些问题&#xff1a; 1、视频格式格式不对&#xff0c;剪辑软件不支持&#xff1b; 2、视频封面不会做&#xff1b; 3、PR导出视频时&#xff0c;没办法做其他事&#xff0c;效率不高&#xff1b; 4、自己配音不好听&#xff0c;配音软件又不好找&#xff1b;…

第14章 局部波动率模型

这学期会时不时更新一下伊曼纽尔德曼&#xff08;Emanuel Derman&#xff09; 教授与迈克尔B.米勒&#xff08;Michael B. Miller&#xff09;的《The Volatility Smile》这本书&#xff0c;本意是协助导师课程需要&#xff0c;发在这里有意的朋友们可以学习一下&#xff0c;思…

影响redis性能的一些潜在因素

影响 Redis 性能的 5 大方面的潜在因素&#xff0c;分别是&#xff1a; Redis 内部的阻塞式操作&#xff1b; CPU 核和 NUMA 架构的影响&#xff1b; Redis 关键系统配置&#xff1b; Redis 内存碎片&#xff1b; Redis 缓冲区。 先学习了解下 Redis 内部的阻塞式操作以及应对的…

【数据架构系列-03】数据仓库、大数据平台、数据中台... 我不太认同《DataFun数据智能知识地图》中的定义

关注DataFunTalk有2年多了&#xff0c;DataFun确实像创始人王大川讲的那样&#xff0c;践行选择、努力和利他原则&#xff0c;专注于大数据、人工智能技术应用的分享与交流&#xff0c;秉承着开源开放的精神&#xff0c;免费的共享了很多有营养的行业实践专业知识&#xff0c;对…

1.win10环境搭建Elasticsearch7.2.0环境

环境介绍jdk1.8安装Elasticsearch7.2.0下载安装包直接解压进入到bin目录&#xff0c;双击elasticsearch.bates启动成功访问http://localhost:9200/jdk版本1.8,很有可能因为jdk版本的问题es启动失败支持连接https://www.elastic.co/cn/support/matrix#matrix_jvm安装Kibana7.2.0…

云计算介绍,让你更了解云计算

同学们好&#xff01; 第一次接触IT行业吗&#xff1f;没关系&#xff0c;看完这篇文章肯定会让你不再陌生。给自己几分钟时间&#xff0c;认真看完哦&#xff01; 1、不知道什么是云计算&#xff1f; 网络计算云计算 官方定义是&#xff1a;通过网络提供可伸缩的分布式计算…

建立相关在线社群的3个简单步骤

在线社群管理和社交媒体营销通常被视为一回事。虽然社群管理确实是社交媒体营销的一个关键部分&#xff0c;但它的意义超越了社交媒体的内容发布。因此&#xff0c;在线社群对于企业的数字营销十分重要。创建、维护和发展社群不是一件容易的工作&#xff0c;也不是一个快速的过…