一、gcc编译器的工作流程

包含四个部分,分别是预处理、编译、汇编和链接。

1. 预处理

在预处理阶段,编译器会将所包含的头文件以及宏定义的值找到并替换成最终的内容,也会将代码的注释部分自动删除,但是预处理之后依旧是一个文本文件,只是这个文件比源文件大得多。以下就是预处理阶段主要做的事情:

(1)将所有的宏定义关键字#define 删除,并且展开所有的宏定义,然后进行字符替换。

(2)处理所有的条件编译指令,包括#ifdef、#ifndef、#endif等

(3)将头文件关键字#include 删除,将#include 指向的文件插入到该行并删除所有的注释内容。

(4)添加行号和文件标识,方便之后的调试。

例如:

1
gcc -E hello.c -o hello.i

这是将源文件hello.c预处理生成hello.i文件

2. 编译

编译阶段就是将预处理好的文件翻译成汇编文件的过程。在这个阶段能够检查出程序是否有错误,并且显示出错误类型。

例如:

1
gcc -S hello.i -o hello.s

将预处理之后的hello.i 文件编译生成hello.s文件。

3. 汇编

汇编过程就是将编译后形成的汇编文件转换为目标文件的过程,即通过汇编阶段生成中间目标文件。

例如:

1
gcc -c hello.s -o hello.o

这一步将编译之后的hello.s文件汇编生成hello.o文件,是二进制格式,这个中间目标文件此时还不能直接运行,需要链接之后才可以被执行。

4. 链接

链接是链接器ld把中间目标文件和相应的库链接为可执行文件,相应库包括静态链接库和动态链接库。

例如:

1
gcc hello.o -o hello

通过以上代码将汇编之后的hello.o文件与相应的库文件链接之后生成可执行文件hello,在运行时只需要调用./hello 即可执行代码。

链接静态库文件成为静态链接,其特点是在编译链接时,把库文件代码全部加入可执行文件中,因此生成的文件代码比较大。

链接动态库文件称为动态链接,其特点是在编译链接时,没有把库文件代码加入到可执行文件中,而是在程序执行时由链接文件加载库,这样可以节省系统的开销,但是运行时需要依赖库文件的支持。

5.总结

gcc编译器在Linux系统下将一个 .c 的源文件经过预处理、编译、汇编和链接四个步骤才能生成可执行文件,需要用到如下命令:

1
2
3
4
gcc -E hello.c -o hello.i	//预处理
gcc -S hello.i -o hello.s //编译
gcc -c hello.s -o hello.o //汇编
gcc hello.o -o hello //链接

二、GDB调试

在Windows系统进行代码调试的时候,因为有IDE集成开发环境的存在,所以调试很方便直观地看到调试信息,但是在Linux系统下没有强大的图像调试工具,这时候就需要借助GDB调试工具。

GDB(GNU Debugger)是一个纯命令行的调试工具,也是有GNU组织开发和维护的。

1. 安装GDB

可以运行以下代码来确认Linux系统上是否安装了gdb调试工具

1
gdb -v

image-20251118105930150

如果出现版本信息,则说明安装了gdb工具

若是没有,可以执行下面的命令进行安装

1
sudo apt-get install gdb

2. GDB调试命令

命令 简写 命令说明
clear 删除设置的断点
break b 设置断点
run r 开始运行程序
next n 执行当前行语句,若该语句为函数调用,不会进入函数内部执行
step s 若有调用函数,进入调用的函数内部;否则,和 next 命令的功能一样
print p 显示变量值,如 p value
continue c 继续程序的运行,直到遇到下一个断点
quit q 退出gdb调试环境

另外还可以使用set var name=value 来设置变量的值。

3. 效果展示

首先在任意的文件夹下新建一个test1.c文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
int Max(int a, int b)
{
int max_value = 0;
max_value = ((a > b) ? a : b);
return max_value;
}
int main(void)
{
int n = 0, m = 0;
printf("intput two numbers please!\n");
scanf("%d %d", &n, &m);
printf("output max value is = %d\n", Max(n, m));
return 0;
}

image-20251118141859183

(1)编译

使用gcc编译器进行编译,注意,要想编译后的程序可以被调试,编译的时候要加上-g 的参数。

1
gcc -g -o test1 test1.c

(2)进入调试模式

编译后之后使用gdb命令进入调试模式

1
gdb test1

image-20251118142416875

(3)run 命令

该命令可以执行整个程序

1
gdb run

image-20251118142634844

(4)break 命令

使用break命令可以设置断点,后面接要打断点的代码行数,如

1
b 5

image-20251118142956225

(5)step 命令

step 命令是单步进行操作,会一行一行地执行代码,如果遇到调用函数,会进入到函数内部。

image-20251118143458586

(6)continue 命令

continue 命令是当程序在某一断点处停止后,用该指令可以继续执行,纸质遇到断点或者程序结束。

image-20251118144036883

(7)设置变量和查看变量操作

image-20251118144935120

next 命令也是一步一步执行代码,但是它与step的不同之处在于它遇到调用的函数不进入函数内部,而step 则是进入到函数内部执行。

print命令则是打印处当前变量的值。

(8)quit 命令

clear命令用来清除所有的断点,quit 命令是用来退出gdb调试模式。

image-20251118151725185