Bootstrap

gcc命令以及GDB调试选项

文章目录

1. 编译过程

2. gcc其他命令

2.1 输出所有警告选项(-Wall)

2.2 头文件选项 -Idirname

2.3 链接库选项

1. 添加库文件搜索目录 (-Ldirname)

2. 加载库名称选项 (-lname)

3. 静态库选项 (-static)

2.4. 代码优化选项

3. GDB调试选项

3.1 run 命令

3.2 list 命令

3.3 设置断点

3.4 跟踪运行结果

1. print 命令

2. display 命令

3. step 和 next 命令

4. continue 命令


1. 编译过程

GCC(GNU Compiler Collection)编译器是一个强大的编译工具链,能够将C/C++源代码转换为可执行的机器码。编译过程分为四个主要步骤:预处理(preprocessing)、编译(compilation)、汇编(assembly)和链接(linking)。

对于这部分的gcc编译过程命令解析在下面篇文章中:

2. gcc其他命令

2.1 输出所有警告选项(-Wall)

-Wall 选项用于显示所有的警告信息,而不仅是显示默认类型的警告。建议使用这个选项以捕捉代码中可能存在的所有问题。现在将上面的 main.c 稍微修改如下:

#include <stdio.h>

#define HUNDRED 100

int main()
{
    int a = 0;
    printf("%d abc\n",HUNDRED);
    return 0;
}

编译不添加 -Wall 选项时,没有任何警告信息,编译结果如下:

$ gcc main.c -o main.c

输出:

$ ./main
100 abc

编译添加 -Wall 选项时,现在所有警告信息显示,编译结果如下:

$ gcc main.c -Wall -o main.c

输出:

main.c: In function ‘main’:
main.c:6:9: warning: unused variable ‘a’ [-Wunused-variable]
    6 |     int a=0;
      |         ^
  1. 使用 -Wall 选项

    • -Wall 选项用于启用所有警告信息,而不仅仅是默认的警告。这有助于发现代码中的潜在问题。
    • 在示例中,main.c 文件中定义了一个未使用的变量 a
  2. 不使用 -Wall 选项

    • 当我们不使用 -Wall 选项进行编译时,编译器不会显示关于未使用变量 a 的警告。
    • 编译和运行结果显示程序正确输出 100 ask
  3. 使用 -Wall 选项

    • 使用 -Wall 选项进行编译时,编译器会显示所有的警告信息,包括未使用变量 a 的警告。
    • 警告信息提示在 main 函数中定义的变量 a 未被使用,建议开发者检查和修正代码。

通过启用 -Wall 选项,我们可以捕捉到更多潜在的代码问题,确保代码质量和可靠性。即使代码能够正常编译和运行,未解决的警告信息也可能导致难以察觉的错误或维护困难。

2.2 头文件选项 -Idirname

dirname 目录加入到头文件搜索目录列表中。当 gcc 在默认的路径中没有找到头文件时,就到本选项指定的目录中去找。

在上面的例子中,我们创建一个 inc 目录,并在里面创建一个头文件 test.h。然后在 main.c 里面增加 #include "test.h"

目录结构如下:

$ tree
.
├── inc
│   └── test.h
└── main.c

1 directory, 2 files

test.h 代码如下:

#ifndef __TEST_H
#define __TEST_H
/*
    code
*/
#endif

运行结果,这样就可以引用制定文件下的目录的头文件,如下:

$ gcc main.c -I inc -o main

如果不添加头文件选项,编译运行结果如下:

$ gcc main.c -o main
main.c:2:18: fatal error: test.h: No such file or directory
compilation terminated.

会产生错误提示,无法找到 test.h 头文件。

  1. 创建目录和头文件

    • 创建一个名为 inc 的目录。
    • inc 目录中创建一个头文件 test.h
    • main.c 文件中包含 #include "test.h"
  2. 使用 -I 选项指定头文件目录

    • 使用 gcc 编译器时,可以通过 -I 选项指定额外的头文件搜索路径。
    • 在示例中,使用 -I inc 指定 inc 目录为头文件搜索路径。
  3. 编译命令和结果

    • 使用命令 gcc main.c -I inc -o main 进行编译,指定 inc 目录为头文件搜索路径。
    • 编译成功后生成可执行文件 main
  4. 不使用 -I 选项的结果

    • 如果不使用 -I 选项,直接运行命令 gcc main.c -o main 进行编译。
    • 编译器会提示找不到 test.h 头文件,编译失败。

通过使用 -I 选项,我们可以指定额外的头文件搜索路径,从而使得编译器能够找到并包含指定目录中的头文件。这对于组织和管理大型项目中的头文件非常有用,可以避免将所有头文件放在同一目录下。

2.3 链接库选项

1. 添加库文件搜索目录 (-Ldirname)

dirname 目录加入到库文件的搜索目录列表中。当 gcc 在默认的路径中没有找到所需的库文件时,就会到此选项指定的目录中去找。

使用示例:

假设有一个自定义的库文件放在 /path/to/libs 目录中,可以使用以下命令指定库文件搜索路径:

gcc main.c -L/path/to/libs -o main

这样,编译器会在 /path/to/libs 目录中搜索所需的库文件。

2. 加载库名称选项 (-lname)

加载名为 libname.alibname.so 的函数库。比如:-lm 表示链接名为 libm.so 的函数库。

使用示例:

假设需要链接数学库 libm,可以使用以下命令:

gcc main.c -lm -o main

编译器会自动找到并链接 libm.solibm.a 库文件。

3. 静态库选项 (-static)

使用静态库。在命令行中,静态库必须放在目标文件之后。

使用示例:

假设有一个静态库文件 libexample.a,并且需要使用静态链接,可以使用以下命令:

gcc test.cpp -o test libexample.a -static

这样,编译器会将 libexample.a 静态库中的所有代码链接到生成的可执行文件 test 中,不再依赖于动态库的存在。

  • -Ldirname:指定库文件的搜索目录。
  • -lname:指定要链接的库名称。
  • -static:使用静态库进行链接。

这些选项可以帮助我们灵活地控制链接过程,选择合适的库文件和链接方式,以满足不同的开发需求。

2.4. 代码优化选项

gcc 提供了不同级别的代码优化方案,用 -Olevel 选项表示。level 取值可以是 0, 1, 2, 3 和 s。默认是 0 级,即不进行优化。

(1) -O0-O1

  • 作用:基本优化,使代码执行的更快。
  • 详细说明
    • -O0:不进行优化,适合调试阶段,因为生成的代码与源代码结构更接近。
    • -O1:进行基本的优化,例如删除无用的代码,简化表达式等,提升程序运行速度,但不会显著增加编译时间。

(2) -O2

  • 作用:产生尽可能小和快的代码。如无特殊要求,不建议使用 -O2 以上的优化。
  • 详细说明
    • -O2:在 -O1 的基础上,进行更多的优化,包括循环优化、函数内联等,进一步提升代码的性能和效率。

(3) -Os

  • 作用:生成最小的可执行文件,适合用于嵌入式软件。
  • 详细说明
    • -Os:在进行 -O2 优化的同时,进一步优化代码大小,去除不必要的代码和数据,以生成更小的可执行文件。
  • -O0:不优化,适合调试。
  • -O1:基本优化,提高执行速度。
  • -O2:更高级的优化,适合追求性能的应用。
  • -Os:优化代码大小,适合嵌入式系统。

不同的优化级别可以根据实际需求进行选择,以达到平衡编译时间、代码性能和可执行文件大小的目的。

3. GDB调试选项

gcc 支持多种调试选项,用于生成调试信息,便于使用调试器(如 GDB)进行调试。常见的调试选项是 -g

  • -g 选项:生成包含调试信息的目标文件或可执行文件,便于调试器(如 GDB)使用这些信息进行源代码级别的调试。

3.1 run 命令

run 命令用于开始执行被调试的程序。执行该命令后,程序将按照设定的断点和调试流程进行执行。run 命令的格式如下:

run [运行参数]

假设已经编译好了一个带有调试信息的可执行文件 main,接下来使用 GDB 进行调试。

  1. 进入调试程序
$ gdb -q main
  • gdb:调用 GDB 调试器。
  • -q:启动 GDB 时不显示版本信息等启动消息,进入安静模式。
  • main:指定要调试的可执行文件。

进入 GDB 后,GDB 会加载调试符号,并显示提示符 (gdb)

  1. 启动调试程序
(gdb) run
  • run:启动被调试的程序。如果程序中设置了断点,会在第一个断点处停下来;如果没有断点,程序将正常执行直到结束或出现错误。

启动调试程序后,GDB 会显示如下信息:

Starting program: /home/ABC/makefile/
abc
[Inferior 1 (process 7425) exited normally]
(gdb)
  • Starting program:表示被调试程序的路径。
  • abc:程序执行的输出结果,说明程序正常运行并输出了预期结果。
  • [Inferior 1 (process 7425) exited normally]:表示被调试的程序(进程 ID 为 7425)正常退出。
  • (gdb):表示 GDB 提示符,等待进一步的调试命令输入。

通过上述步骤和解释,可以看出如何使用 GDB 的 run 命令启动调试程序,并查看程序的执行结果。

3.2 list 命令

list 命令用于查看源代码及其对应的行号信息。通过使用 list 命令,调试人员可以在调试过程中方便地查看源代码的内容。list 命令的格式如下:

list [行号]

以下是如何使用 list 命令查看源代码的具体操作和解释:

  1. 列出第一行附近的源码
(gdb) list 1

list 1:列出包含第 1 行代码在内的前后各 5 行,共 10 行源码。

#include <stdio.h>

#define a 66

int main()
{
    int b = 66;
    printf("%d abc\n", a);
    return 0;
}

在 GDB 中执行 list 1 命令后,显示的代码如下所示:

(gdb) list 1
1   #include <stdio.h>
2
3   #define a 66
4
5   int main()
6   {
7       int b = 66;
8
9       printf("%d ask\n", HUNDRED);
10      return 0;
11  }

该命令会列出第 1 行代码周围的 10 行代码,包括代码和行号信息。

  1. 继续查看下 10 行源码

    当需要继续查看下 10 行代码时,只需按 Enter 键:

(gdb) <Enter>

GDB 将继续显示下一段代码。例如:

12
13  (下一段代码...)

通过上述步骤,list 命令可以方便地在调试过程中查看代码的具体内容和行号,有助于调试人员理解代码的执行情况和定位问题。

3.3 设置断点

在调试过程中,设置断点是一项非常重要的功能。通过断点,可以暂停程序的执行,以便检查程序的状态、变量值等,从而更好地理解程序的运行过程,找出问题所在。以下是关于如何在 GDB 中设置、查看和删除断点的详细解释。

1. 设置断点

使用 break 命令可以在指定的行号或函数名处设置断点。格式如下:

break <行号> | <函数名>

示例

(gdb) break 7

结果:

Breakpoint 1 at 0x40052e: file main.c, line 7.

上面的命令在 main.c 文件的第 7 行设置了一个断点,GDB 返回了该断点的编号和具体位置。

2. 查看断点

使用 info break 命令可以查看当前所有的断点。格式如下:

info break

示例

(gdb) info break

结果:

Num  Type           Disp Enb Address            What
1    breakpoint     keep y   0x000000000040052e in main at main.c:7

上述输出显示了当前存在一个断点,其编号为 1,类型为 breakpoint,状态为 keep,启用状态为 y,断点地址为 0x000000000040052e,所在位置为 main.c 文件的第 7 行。

3. 删除断点

使用 delete 命令可以删除指定的断点。格式如下:

delete <断点号>

示例

(gdb) delete breakpoint 1

结果:

(gdb) info break
No breakpoints or watchpoints.

在上述示例中,delete breakpoint 1 命令删除了编号为 1 的断点。使用 info break 命令再次查看时,显示当前没有任何断点或观察点。

通过上述步骤,可以在 GDB 中方便地设置、查看和删除断点,有助于更好地调试程序。

3.4 跟踪运行结果

在调试过程中,GDB 提供了一些命令可以帮助我们跟踪程序的运行,查看变量的值,逐步执行代码等。以下是一些常用的命令及其使用示例:

1. print 命令

print 命令用于显示变量的值。格式如下:

print [/格式] <表达式>

示例:

(gdb) print b
$1 = 66

上面的命令显示了变量 b 的值是 66。

2. display 命令

display 命令用于设置自动现实命令,每当程序暂停时都会显示指定的表达式的值。格式如下:

display <表达式>
3. stepnext 命令

stepnext 命令用于逐步执行代码。step 命令会进入函数内部执行,而 next 命令则会跳过函数内部的执行,直接到下一行代码。格式如下:

step [行号]
next [行号]
4. continue 命令

continue 命令用于继续执行程序,直到遇到下一个断点。格式如下:

continue

下面是一个完整的调试示例,展示了如何设置断点、运行程序、显示变量值等操作:

(gdb) break 7
Breakpoint 1 at 0x40052e: file main.c, line 7.
(gdb) break 9
Breakpoint 2 at 0x400535: file main.c, line 9.
(gdb) run
Starting program: /home/100ask/makefile/

Breakpoint 1, main () at main.c:7
7           int b = 66;
(gdb) continue
Continuing.

Breakpoint 2, main () at main.c:9
9           printf("%d abc\n",a);
(gdb) print b
$1 = 66
(gdb)

设置断点

  • break 7:在 main.c 文件的第 7 行设置断点,GDB 返回断点编号和位置。
  • break 9:在 main.c 文件的第 9 行设置断点。

运行程序

  • run:启动程序运行,程序会运行到第一个断点处暂停。

继续执行程序

  • continue:继续执行程序,直到遇到下一个断点。

显示变量值

  • print a:显示变量 b 的值,结果为 66。

通过这些命令,可以有效地跟踪和调试程序的运行情况,发现并解决程序中的问题。

;