1、gcc的四个阶段
预处理
- 预处理功能主要包括宏定义,文件包含,条件编译,去注释
- 预处理指令是以#号开头的代码行
- 例子:gcc -E hello.c -o hello.i
- 选项:-E 该选项的作用是让gcc在预处理之后停止编译过程
- 选项:-o是指目标文件,i文件指的是已经处理过的C原始程序
编译
- 在这个阶段中,gcc首先要检查代码的规范性。是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,将代码编译成汇编语言
- 可以使用-S选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码
- 例子:gcc -S hello.i -o hello.s
汇编
- 汇编阶段是把编译阶段生成的.s文件转成目标文件
- 可以使用-c就可以看到汇编代码已转化成.o的二进制目标代码了
- 例子 gcc hello.o -o hello
链接
在成功编译之后,就进入了链接阶段
实例:gcc hello.o -o hello
补充知识:函数库
比如说:我们只使用的 stdio.h 中只包含了对于printf函数的声明,却没有说明对该函数的实现,那么系统是怎么实现printf函数呢?
答案是:系统把这些函数的实现都被收入到了名为libc.so.6的库文件中去了,在没有特别指定时,gcc会到系统默认的搜索路径“/user/lib”下进行查找,也就是链接到libc.so.6的库函数中去,这就是链接的作用
并且,函数库分为静态库和动态库两种
- 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了,其后缀名一般是.a
- 动态库是相反的,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样写可以节省系统的开销。动态库一般后缀名为.so,如前面的libc.so.6就是动态库。gcc在编译时默认使用动态库。完成编译链接之后,gcc就可以生成可执行文件。
gcc选项
- -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
- -S编译到汇编语言不进行汇编和链接
- -c编译到目标代码
- -o文件输出到文件
- -static此选项对生成的文件采用静态链接
- -g生成调试信息。GNU调试器可以利用该信息
- -shared此选项将尽量使用动态库,所以生成的文件比较小
- -O0
- -O1
- -O2
- -O3
- 这四个是编译器优化选项的四个级别,-O0表示没有优化,O1为缺省值,-O3为优化级别最高
- -w不生成任何警告信息
- -Wall生成所有警告信息
gdb的使用
背景
- 程序的发布方式有两种,debug模式和release模式
- Linux gcc/g++出来的二进制程序中,默认是release模式
- 要使用gdb调试,必须在源代码生成的二进制程序的时候,加上-g选项
使用
gdb binfile 退出:ctrl+d或quit调试命令
- list + 行号:显示binfile的源代码,接着上次的位置往下列,每次列出10行
- list + 函数名:列出某个函数的源代码
- r 或者 run:运行程序
- n 或 next:单条执行
- s 或 step:进入函数调用
- break(b) 行号:在某一行设置断点
- break 函数名:在某一个函数开头设置断点
- info break:查看断点信息
- finish:执行到当前函数返回,然后停下来等待命令
- printf(p):打印表达式的值,通过表达式可以修改变量的值或者调用函数
- p 变量:打印变量值
- set var:修改变量的值
- continue(或者c):从当前位置开始连续而非单步执行程序
- delete breakpoints:删除所有断点
- delete breakpoints n:删除序号为n的断点
- disable breakpoints:禁用断点
- enable breakpoints:启用断点
- info(或i) breakpoints:查看当前设置了哪些断点
- display 变量名:跟踪查看一个变量,每次停下来都显示它的值
- undisplay:取消对先前设置的那些变量的跟踪
- until X行号:跳到X行
- breaktrace(或bt):查看各级函数调用及参数
- info (i) locals:查看当前栈帧局部变量的值
- quit:退出gdb