前言
本文总结了基本的Linux下GCC编译器,GDB调试器及Makefile的使用方法。
目录
一、GCC
1.GCC的介绍
GCC(GNU Compiler Collection,GNU 编译器套装)是一套由 GNU 开发的编程语言编译器。GCC 是非常优秀的跨平台编译器集合,支持 x86、ARM、MIPS 和 PowerPC 等多种目标平台。GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理 C 语言。GCC 很快地扩展,变得可处理 C++。之后也变得可处理 Fortran、Pascal、Objective-C、Java,以及 Ada 与其他语言。
2.GCC的使用
(1).GCC指令的一般格式
gcc [选项] 要编译的文件列表 [-o 目标文件]
//目标文件可缺省,gcc默认生成可执行的文件为a.out
//单文件编译
gcc test.c
//多文件编译
gcc main.c add.c -o main
(2).GCC的选项
gcc有超过100个的可用选项,主要包括总体选项、告警和出错选项、优化选项和体系结构相关选项。下表为常用选项。
(3).GCC常见的后缀名及其含义
(4).gcc的执行过程
虽然称gcc是C语言的编译器,但使用gcc由C语言源代码文件生成可执行文件的过程不仅仅是编译的过程,而是要经历四个相互关联的步骤:预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking),如图所示。
a.预处理(preprocess)
这个阶段主要处理源文件中的 #ifdef、#include和 #define命令。对于该阶段,gcc将stdio.h文件中的代码包含进这段程序,我们可以利用
gcc -E test.c -o test.i //test为生成的.c文件名
命令来生成预处理过的.i文件。-E选项代表让gcc在预处理阶段后停止编译。
预处理规则
- 删除代码中的“#define”,展开所有宏定义;
- 处理条件编译指令,如#if、#ifdef、#undef等;
- 将由“#include”包含的文件插入到预编译指令对应的位置,若文件中包含其它文件,同样进行替换;
- 删除代码中的注释;
- 添加行号和文件标识;
- 保留#pragma编译器指令。
b.编译(Compilation)
这个阶段gcc会对经过预处理的文件进行语法、词法和语义分析,确定代码实际要做的工作,若检查无误,则生成相应的汇编代码文件。
主要作用是对预编译后的.i文件编译,生成汇编代码的.s文件。
gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。我们可以使用-S选项来进行查看,该选项只进行编译而不进行汇编过程,生成汇编代码。可以利用
gcc -S test.i -o test.s
命令进行编译过程。
c.汇编(Assembly)
汇编过程将编译后生成的汇编代码转换为机器可以执行的命令,即二进制指令,每一个汇编语句几乎都会对应一条机器指令。该阶段是将编译后的.s文件转化成二进制文件.o的过程,利用-c选项就可以生成二进制.o文件。可以利用
gcc -c test.s
生成二进制代码test.o或利用
gcc -c test.s -o t.o
生成指定名称二进制代码t.o。
d.链接(Linking)
链接过程是组装各个目标文件的过程,在这个过程中会解决符号依赖和库依赖关系,最终生成可执行文件。该阶段主要将成功编译的二进制文件进行链接操作,生成可执行文件。利用
gcc test.o -o test
生成可执行文件test。
e.运行
./test
f.执行效果
二、GDB
1.GDB的介绍
GDB可以逐条执行程序、操控程序的运行,并且随时可以查看程序中所有的内部状态,如各变量的值、传给函数的参数、当前执行的语句位置等,藉此判断代码中的逻辑错误。
所有的程序在写好以后,都要经过调试,在调试过程中发现并改正程序中的错误。如果没有GDB,程序员为了跟踪某些错误,就要在程序中加入大量的语句,用来产生一些特定的输出。对于某些程序来说,这样做会导致更多的错误。
2.GDB的使用
(1).启用GDB
//例:编写程序源代码为hello.c
gcc hello.c -o hello -g // -g 在代码中加入调试信息
gdb hello [-选项] //启动GDB并载入程序
(2).GDB常用调试命令
(3).TUI模式
TUI(terminal user interface,终端用户界面)模式,比较直观的显示参数,按组合键Ctrl+X+A切换。
//改变窗口布局
layout prev | next | LAYOUT-NAME
src:Displays source and command windows. // 显示源码和命令窗口。
asm:Displays disassembly and command windows. // 显示汇编和命令窗口。
split:Displays source, disassembly and command windows. // 显示源码、汇编和命令窗口。
regs:Displays register window. // 显示寄存器窗口。
(4).常见使用步骤
(1)gdb程序
(2)列出程序代码,并设置断点
(3)运行程序
(4)查看变量值
(5)设置监听变量
(6)继续执行
(7)结束调试
三、Makefile
1.基本概念
Makefile是GNU make程序在执行的时候默认读取的配置文件。Makefile记录了文件之间的依赖关系,通过比对目标文件和依赖文件的时间戳,决定是否需要执行相应的命令;同时,Makefile还可以定义变量,接收用户传递的参数变量,通过这些元素的相互配合,省去了繁杂的编译命令,不仅节省时间,也减小了出错的概率。
Makefile主要包含了以下五个部分:显式规则、隐晦规则、变量的定义、文件指示、 注释。
Makefile文件内容的主体由很多规则构成,每一条规则都由三部分组成:
(1)目标(Object):表明输出的目标
(2)依赖(Dependency):输出目标的依赖对象
(3)命令(Command):生成目标需要执行的命令
make是一个命令工具,用于解释Makefile中的指令。Makefile是一个文件,用来告诉make命令如果编译整个工程生成程序集。只需编写Makefile文件,然后再Makefile所在目录执行make命令即可。
Make的工作流程
① 查找当前目录下的Makefile文件;
② 初始化Makefile文件中的变量;
③ 分析Makefile中的所有规则;
④ 为所有的目标文件创建依赖关系;
⑤ 根据依赖关系决定哪些目标文件要重新生成;
⑥ 执行生成命令。
2.使用方法
(1).基本格式
a.make命令
//make命令
make [选项] [Makefile文件]
make clean
b.Makefile文件
//Makefile 文件格式
目标 .. : 依赖 ...
<Tab>命令 //命令前必须是Tab,否则会出错
# 解释格式:[符号] [作用]
# # 注释
# [] 可替代的内容
# ?= 如果没有被赋值过就赋予等号后面的值
# += 添加等号后面的值
# = 最基本的赋值(最后才展开)
# 自动变量
# $@ 指代当前目标,make 命令当前构建的那个目标。比如,make foo 的 $@ 就指代 foo。
# $< 指代第一个依赖条件。比如,规则为 t: p1 p2,那么 $^ 就指代 p1 p2 。
# $^ 指代所有前置条件,之间以空格分隔,不包含重复的依赖条件。
# $* 指代匹配符 % 匹配的部分,即不包含扩展名的目标文件名称。比如 %.txt 匹配 f1.txt 中的 f1 ,$* 就表示 f1。
# version 1
[可执行文件名]:[1.cpp] [2.cpp] [3.cpp] [...]
gcc/g++ -o [可执行文件名] [1.cpp] [2.cpp] [3.cpp] [...]
#version 2
[变量例:CC] = g++/gcc
[变量例:TARGET] = [可执行文件名]
[变量例:OBJ]: [链接文件:1.o] [2.o] [...]
$(TARGET) : $(OBJ)
$(CC) -o $(TARGET) $(OBJ)
[1.o]: [1.cpp]
$(CC) -c [1.cpp]
[1.o]: [1.cpp]
$(CC) -c [1.cpp]
[...]
#version 3
[变量例:CC] = g++/gcc
[变量例:TARGET] = [可执行文件名]
[变量例:OBJ]: [链接文件:1.o] [2.o] [...]
[变量例:FLAGS] = [编译选项:-c -Wall] # -Wall 警告
$(TARGET) : $(OBJ)
$(CC) -o $@ $^
%.o:%.cpp
$(CC) $(FLAGS) $< -o $@
.PHONY clean #防止存在文件名为clean
clean:
rm -f *.o $(TARGET)
#version4
[变量例:CC] = g++/gcc
[变量例:TARGET] = [可执行文件名]
[变量例:SRC] = $(wildcard *.cpp)
[变量例:OBJ] = $(patsubst %.cpp,%.o,$(SRC))
[变量例:FLAGS] = [编译选项:-c -Wall]
$(TARGET) : $(OBJ)
$(CC) -o $@ $^
%.o:%.cpp
$(CC) $(FLAGS) $< -o $@
.PHONY clean #防止存在文件名为clean
clean:
rm -f *.o $(TARGET)
c. 执行效果