文章目录
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;
| ^
-
使用
-Wall
选项:-Wall
选项用于启用所有警告信息,而不仅仅是默认的警告。这有助于发现代码中的潜在问题。- 在示例中,
main.c
文件中定义了一个未使用的变量a
。
-
不使用
-Wall
选项:- 当我们不使用
-Wall
选项进行编译时,编译器不会显示关于未使用变量a
的警告。 - 编译和运行结果显示程序正确输出
100 ask
。
- 当我们不使用
-
使用
-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
头文件。
-
创建目录和头文件:
- 创建一个名为
inc
的目录。 - 在
inc
目录中创建一个头文件test.h
。 - 在
main.c
文件中包含#include "test.h"
。
- 创建一个名为
-
使用
-I
选项指定头文件目录:- 使用
gcc
编译器时,可以通过-I
选项指定额外的头文件搜索路径。 - 在示例中,使用
-I inc
指定inc
目录为头文件搜索路径。
- 使用
-
编译命令和结果:
- 使用命令
gcc main.c -I inc -o main
进行编译,指定inc
目录为头文件搜索路径。 - 编译成功后生成可执行文件
main
。
- 使用命令
-
不使用
-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.a
或 libname.so
的函数库。比如:-lm
表示链接名为 libm.so
的函数库。
使用示例:
假设需要链接数学库 libm
,可以使用以下命令:
gcc main.c -lm -o main
编译器会自动找到并链接 libm.so
或 libm.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 进行调试。
- 进入调试程序:
$ gdb -q main
gdb
:调用 GDB 调试器。-q
:启动 GDB 时不显示版本信息等启动消息,进入安静模式。main
:指定要调试的可执行文件。
进入 GDB 后,GDB 会加载调试符号,并显示提示符 (gdb)
。
- 启动调试程序:
(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
命令查看源代码的具体操作和解释:
- 列出第一行附近的源码:
(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 行代码,包括代码和行号信息。
-
继续查看下 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. step
和 next
命令
step
和 next
命令用于逐步执行代码。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。
通过这些命令,可以有效地跟踪和调试程序的运行情况,发现并解决程序中的问题。