【Linux】基础:Linux环境基础开发工具——gcc与gdb

【Linux】基础:Linux环境基础开发工具——gcc 与 gdb

摘要:在本篇博客中,将会介绍如何使用gcc或者g++来编译代码,使用gdb来调试代码。对于gcc的内容,将会从对于C语言程序,如何从源文件不断转换成可执行文件进行分析。其中主要进行的步骤为:预处理、编译、汇编、链接。在此也会对其中每个过程进行简单介绍。而对于gdb的内容,将会主要从方法上介绍如何对相关代码进行调试,主要掌握调试的流程。


一. 概述

Linux作为一个操作系统,有着自己开发程序的方式。各位都知道,Linux主要面向的是像程序员这样的群体,因此缺少像VS或者Pycharm等那样的庞大的商用的集成开发工具。不过Linux也是可以做到,像其他IDE那样进行开发,主要的实现方式就是通过指令的形式进行完成。

Linux环境基础开发工具包括六大板块:

  • 学习yum工具,进行软件安装
  • 掌握vim编辑器使用,学会vim的简单配置
  • 掌握gcc/g++编译器的使用,并了解其过程,原理
  • 掌握简单gdb使用于调试
  • 掌握简单的Makefile编写,了解其运行思想
  • 学习 git 命令行的简单操作, 能够将代码上传到码云上

最后,还会通过使用Linux环境下基础开发工具,完成一个C语言的小示例——进度条

如上篇博客提到,开发过程是有固定的模式的,可以分为:编写代码、编译代码、调试代码、发布代码与运行、代码管理与维护。在本篇博客中,将会介绍如何使用gcc或者g++来编译代码,使用gdb来调试代码.

对于gcc的内容,将会从对于C语言程序,如何从源文件不断转换成可执行文件进行分析。其中主要进行的步骤为:预处理、编译、汇编、链接。在以往的博客中对其中的过程进行了具体介绍,连接为:【C/C++】基础:程序环境与预处理。在此也会对其中每个过程进行简单介绍。

而对于gdb的内容,将会主要从方法上介绍如何对相关代码进行调试,主要掌握调试的流程。

二.gcc

2.1 概述

gcc主要作用是进行编译c语言文件其命令格式为:gcc [选项] 要编译的文件 [选项] [目标文件],对于其默认形式不带任何选项,可以直接生成可执行文件,为了进行相关实验,先进行一段简单代码书写,并通过gcc进行编译,示例如下:

[lht@VM-12-7-centos Blog_Develope]$ cat test.c 
#include<stdio.h>
int main(){
        printf("hello gcc\n");
        //printf("hello gcc\n");
        //printf("hello gcc\n");
        //printf("hello gcc\n");
        printf("hello gcc\n");
        printf("hello gcc\n");
        return 0;
}
[lht@VM-12-7-centos Blog_Develope]$ gcc test.c 
[lht@VM-12-7-centos Blog_Develope]$ ./a.out 
hello gcc
hello gcc
hello gcc

2.2 预处理

选项:-E

预处理的主要作用为:头文件展开,去注释,条件编译,宏替换等。对于选项-E,其表示的意思为开始进行编译,在预处理后,停止编译,生成文件

命令示例: gcc -E test.c -o test.i

补充:选项-o是指目标文件,.i文件为已经过预处理的C原始程序

预处理后打开相应文件进行展示,需要注意相应变化,展示了预处理的部分功能,图示如下:

image-20221029224259841

2.3 编译

选项:-s

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

对于-s选项,其表示意思为,对才代码进行翻译处理,当完成编译后停止下来,所生成的代码为汇编代码。

命令示例:gcc -s test.i -o test.s

编译后生成文件部分代码如下:

	.file	"test.c"
	.text
	.section	.rodata
.LC0:
	.string	"Num = %d\n"
.LC1:
	.string	"hello gcc"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	......

2.3 汇编

可能有人认为汇编语言是比较接近底层的语言的,可是这只是对于人来说,而对于计算机来说,计算机只能看懂二进制编码,因此计算机不可以直接执行汇编代码,汇编代码还需要再次进行转换成为二进制代码,才能被计算机直接执行,而这一个过程被称为汇编。因此汇编的作用为:生成机器可识别的代码

选项:-c

选项表示的意思为:开始进行程序翻译,完成汇编工作后停止,生成目标文件,选项-c就可看到汇编代码已转化为.o的二进制目标代码

注意:生成后的二进制目标代码虽然计算机可以直接读懂,但无法直接执行,因为还需要进行链接这一步骤,这样才能对库函数进行调用,程序才会完整。

命令示例:gcc -c test.s -o test.o

2.5 链接

要学习关于gcc 的链接步骤关键是理解链接的含义。从链接的作用来理解,可以预想两个场景,首先对于库函数的使用,在自身写的程序中,离不开对于库函数的调用,而自身程序并不存在对于库函数的实现,因此需要调用外部库进行实现自身程序的库函数,其次在编写程序时,离不开多人协助的工作,可能会存在调用其他人的程序,这时也需要链接完成调用工作。总的来说,链接就是将自身代码与外部库外部数据链接起来

命令示例:gcc test.o -o test

示例如下:

[lht@VM-12-7-centos Blog_Develope]$ gcc test.o -o test
[lht@VM-12-7-centos Blog_Develope]$ ./test
Num = 10
hello gcc
hello gcc
hello gcc

在此对链接进行更深入的理解:实际上c语言包括了内部库和外部库,静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了,其后缀名一般为.a;动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销,动态库一般后缀名为.so

在Linux中,可以通过命令ldd来查看多个库的依赖关系,如对于编译后的test可执行文件,可以发现其与动态库libc.so.6完成了链接,在Linux上默认也是使用动态链接的方式完成链接

[lht@VM-12-7-centos Blog_Develope]$ ldd test
        linux-vdso.so.1 (0x00007fff43bfd000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f4af1d1d000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f4af20e2000)

当使用选项将链接方式设置为与静态库链接时(选项最后加上-static),再次查看依赖关系,可以发现此时依赖关系发生了改变,同时对于编译生成的可执行文件,由于静态库的链接导致文件变得较为庞大。

2.6 记忆技巧

  • 对于预处理、编译、汇编的选项为:ESc,恰好对应退出键的Esc,但需要注意大小写的区分。
  • 对于生成后的文件后缀分别为:iso,与IOS系统非常相似,可以来辅助记忆。

2.7 补充:为什么C程序的翻译是如此过程?

这个问题与计算机的发展史有关,最开始是通过纸带通过打点的方式表示二进制0和1,随着时间的发展,发现通过纸带来表示的二进制代码过于麻烦,于是采取了汇编的策略来简化代码,在随着时间的推移更高级的语言不断诞生,最后成为了我们熟知的c语言。而语言的发展不是一蹴而就的,是慢慢推演发展而成,每次发展都会对历史语言进行继承或者兼容,从而形成了税计算机发展推演而成的固定的翻译步骤。

2.8 g++

如果需要写c++文件时,可以使用g++来完成编译生成可执行文件,由于大体过程与gcc相似,因此在此只进行举例,不再进行过多赘述。

[lht@VM-12-7-centos Blog_Develope]$ touch test.cpp
[lht@VM-12-7-centos Blog_Develope]$ vim test.cpp 
[lht@VM-12-7-centos Blog_Develope]$ cat test.cpp 
#include<iostream>
using namespace std;
int main(){
        cout<<"hello g++!\n";
        return 0;
}
[lht@VM-12-7-centos Blog_Develope]$ g++ test.cpp -o test_cpp
[lht@VM-12-7-centos Blog_Develope]$ ./test_cpp 
hello g++!

三. gdb调试

在学习C语言的过程中,一般都会使用VS作为集成开发工具,其中就包括了调试的功能,同样的在Linux中,可以使用gdb进行调试。在以往在VS中调试代码的过程中, 大家都知道需要在程序发布debug模式下才能进行调试,而在Linux下也是需要的,特别注意的是在Linux中,默认是生成realease模式,所以在编译时需要通过-g选项,生成debug版本,命令示例如下:gcc test.c -o test -g

在此还补充一下debug版本与realease版本的区别,实际上对于debug的发行版本,会在其二进制文件中加入一定的调试信息,可以通过命令readelf -S filename来查看相应的结构,可以发现会多出debug信息,示例如下:

[lht@VM-12-7-centos Blog_Develope]$ readelf -S test | grep debug
  [27] .debug_aranges    PROGBITS         0000000000000000  00002be4
  [28] .debug_info       PROGBITS         0000000000000000  00002c14
  [29] .debug_abbrev     PROGBITS         0000000000000000  00002f15
  [30] .debug_line       PROGBITS         0000000000000000  00002fe1
  [31] .debug_str        PROGBITS         0000000000000000  000030d8

3.1 开始与结束

开始命令:gdb filename

结束命令:quit

示例如下:

[lht@VM-12-7-centos Blog_Develope]$ gdb test
GNU gdb (GDB) Red Hat Enterprise Linux 8.2-16.el8
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from test...done.
(gdb) quit

3.2 常见指令

运行

  • r或run:运行程序;从开始连续而非单步执行程序
  • fifinish:执行到当前函数返回,然后挺下来等待命令
  • continue(或c):从当前位置开始连续而非单步执行程序
  • until X行号:跳至X行

小节:finsih是结束当前函数,continue是直接到达下一个断点,until跳转到指定行。

代码显示

  • list/l 行号:显示二进制文件源代码,接着上次的位置往下列,每次列10行。
  • list/l 函数名:列出某个函数的源代码

断点相关

  • break(b) 行号:在某一行设置断点
  • break 函数名:在某个函数开头设置断点
  • info break :查看断点信息。
  • delete(d) breakpoints :删除所有断点
  • delete(d) breakpoints n:删除序号为n的断点
  • disable breakpoints:禁用断点
  • enable breakpoints:启用断点

逐语句逐过程

过程:函数看作一个整体

  • n 或 next:单条执行(逐语句)
  • s或step:进入函数调用(逐过程)

监视

  • display 变量名:跟踪查看一个变量,每次停下来都显示它的值
  • undisplay:取消对先前设置的那些变量的跟踪
  • print§:打印表达式的值,通过表达式可以修改变量的值或者调用函数

调用栈帧

  • breaktrace(或bt):查看各级函数调用及参数
  • info(i) locals:查看当前栈帧局部变量的值

设置值

  • set var:修改变量的值

补充:

  1. 代码将会放到:Linux_Review: Linux博客与代码 (gitee.com) ,欢迎查看!
    print§:打印表达式的值,通过表达式可以修改变量的值或者调用函数

调用栈帧

  • breaktrace(或bt):查看各级函数调用及参数
  • info(i) locals:查看当前栈帧局部变量的值

设置值

  • set var:修改变量的值

补充:

  1. 代码将会放到:Linux_Review: Linux博客与代码 (gitee.com) ,欢迎查看!
  2. 欢迎各位点赞、评论、收藏与关注,大家的支持是我更新的动力,我会继续不断地分享更多的知识!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fat one

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值