文章目录
1 编译源程序
这里以C程序为例。
1.1 什么是gcc
原名为GNU C语言编译器(GNU C Compiler),因为它原本只能处理C语言。GCC在发布后很快地得到扩展,变得可处理C++。之后也变得可处理Fortran、Pascal、Objective-C、Java、Ada,Go与其他语言。
后更名为GNU编译器套装(GNU Compiler Collection),指一套编程语言编译器,以GPL及LGPL许可证所发行的自由软件,也是GNU计划的关键部分,是GNU工具链的主要组成部分之一。
1.2 gcc编译C程序的过程
总共分为四个阶段:预处理、编译、汇编、链接
- 预处理:展开以#起始的行,如处理宏定义#define、源文件包含#include、条件编译#if/#ifdef/#ifndef/#else/#elif/#endif……
- 编译:进行词法、语法分析等
- 汇编:将编译生成的汇编代码转变成目标代码
- 链接:链接所有目标代码生成可执行文件
1.3 使用gcc编译只有一个源文件的C程序
选项 | 作用 |
---|---|
-E | 仅执行预编译 |
-S | 仅执行汇编 |
-c | 仅执行编译,不进行链接 |
-o 文件名 | 生成名为 文件名 的文件 |
语法格式:gcc (选项)(参数)
main.c
#include <stdio.h>
int main(int argc, char const *argv[])
{
for (int i = 0; i < 5; i++)
{
printf("Hello World!\n");
}
return 0;
}
1、直接生成可执行文件
PS F:\program_files\workspace\vscode\c\hello> gcc .\main.c -o main
PS F:\program_files\workspace\vscode\c\hello> .\main.exe
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
2、分步编译。执行相应的命令会生成对应的中间文件
PS F:\program_files\workspace\vscode\c\hello> gcc -E .\main.c -o .\main.i
PS F:\program_files\workspace\vscode\c\hello> gcc -S .\main.i -o .\main.s
PS F:\program_files\workspace\vscode\c\hello> gcc -c .\main.s -o .\main.o
PS F:\program_files\workspace\vscode\c\hello> gcc .\main.o -o .\main
PS F:\program_files\workspace\vscode\c\hello> .\main.exe
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
2 使用Makefile编译多个源文件的C程序
使用Makefile可以较为方便的编译有多个源文件的程序,不使用Makefile的话,需要先将每个源文件分别编译,最后再链接到一起,这样比较麻烦。
hello.h
#ifndef HELLO_H_
#define HELLO_H_
#include<stdio.h>
void PrintHello();
#endif
hello.c
#include "hello.h"
void PrintHello()
{
for (int i = 0; i < 5; i++)
{
printf("Hello World!\n");
}
}
main.c
#include "hello.h"
int main(int argc, char const *argv[])
{
PrintHello();
return 0;
}
1、不使用Makefile
一次编译所有源文件
PS F:\program_files\workspace\vscode\c\hello> gcc .\main.c .\hello.c -o main
PS F:\program_files\workspace\vscode\c\hello> .\main.exe
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
单独编译每个源文件
PS F:\program_files\workspace\vscode\c\hello> gcc -c .\main.c -o main.o
PS F:\program_files\workspace\vscode\c\hello> gcc -c .\hello.c -o hello.o
PS F:\program_files\workspace\vscode\c\hello> gcc .\main.o .\hello.o -o main
PS F:\program_files\workspace\vscode\c\hello> .\main.exe
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
2、使用Makefile
Makefile语法规则
target ... : prerequisites ...
command
...
...
注意:在Makefile中的命令,缩进必须要以 Tab
键开始;而注释以#
开始;文件名应为Makefile
或makefile
,Makefile
是更为标准的写法
- target可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)。
- prerequisites是要生成那个target所需要的文件或是目标。
- command也就是make需要执行的命令。
这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在 command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是makefile的规则。也就是makefile中最核心的内容。根据上述规则,就可以写出Makefile了:
main: main.o hello.o
gcc main.o hello.o -o main
main.o: main.c hello.h
gcc -c main.c
hello.o: hello.c hello.h
gcc -c hello.c
clean:
rm main.exe *.o
生成 main.exe 要依赖 main.o hello;生成 main.exe 的规则是 gcc main.o hello.o -o main
生成 main.o 要依赖 hello.h main.c;生成 main.o 的规则是 gcc -c main.c
生成 hello.o 要依赖 hello.h hello.c;生成 hello.o 的规则是 gcc -c hello.c
clean : 删除中间文件 *.o 和执行文件
每个Makefile中都应该写一个清空目标文件( .o 和执行文件)的规则,在不需要这些文件的时候,只需要执行make clean
就行了。这不仅便于重编译,也很利于保持文件的清洁。
PS F:\program_files\workspace\vscode\c\hello> make
gcc -c main.c
gcc -c hello.c
gcc main.o hello.o -o main
PS F:\program_files\workspace\vscode\c\hello> .\main.exe
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
3 使用gdb调试程序
test.cpp
#include <iostream>
#include <iterator>
using namespace std;
void select_sort(int array[], int len);
void traverse(int array[], int len);
int a[] = {3, 55, 0, 1, -4};
int main(void)
{
int a[] = {9, 22, 33, 11, 3};
int len = sizeof(a) / sizeof(int);
select_sort(a, len);
select_sort(::a, len);
traverse(a, len);
traverse(::a, len);
return 0;
}
void select_sort(int array[], int len)
{
int i, j, k;
int a[] = {109, 422, 4, 1, 0};
for (i = 0; i < len; i++) {
k = i;
for (j = i; j < len; j++) {
if (array[j] < array[k]) {
k = j;
}
}
int temp = array[k];
array[k] = array[i];
array[i] = temp;
}
}
void traverse(int a[], int len)
{
for (int i = 0; i < len; i++) {
cout << a[i] << ' ';
}
cout << endl;
}
PS C:\Users\fy\Desktop> g++ -g test.cpp -o test
PS C:\Users\fy\Desktop> gdb -q test.exe
执行上述命令后通过run [args]
命令开始调试,操作完成后通过q
命令退出调试。
3.1 断点
功能 | 命令 | 简写 |
---|---|---|
1、设置断点 | break 函数名 | b 函数名 |
break 行号 | b 行号 | |
break 文件名:函数名 | b 文件名:函数名 | |
break 文件名:行号 | b 文件名:行号 | |
2、查看所有断点 | info breakpoints | i b |
3、删除指定断点 | delete 断点id | d 断点id |
clear 函数名 | ||
clear 行号 | ||
clear 文件名:函数名 | ||
clear 文件名:行号 | ||
clear | ||
4、删除所有断点 | delete | d |
3.2 栈帧
程序执行时,每调用一个函数,便将函数压入栈中,最先执行的函数在栈底,最晚执行的函数在栈顶。可以通过查看栈帧了解函数调用信息。
功能 | 命令 | 简写 | |
---|---|---|---|
1、显示所有栈帧 | backtrace | bt | |
backtrace full | bt full | 不仅显示栈帧,还显示每个栈帧内的局部变量 | |
2、显示前N个栈帧 | backtrace N | bt N | |
backtrace full N | bt full N | ||
3、切换栈帧 | frame 栈帧id | f 栈帧id |