05gcc/g++和gdb使用

Posted 精致的灰(>_<)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了05gcc/g++和gdb使用相关的知识,希望对你有一定的参考价值。

一般生成C/C++可执行程序需要经过以下四个步骤:1.预处理(头文件展开、去注释、宏替换、条件编译)。2.编译(C代码翻译成汇编语言)。3.汇编(汇编代码转为二进制目标代码)。4.链接(将目标文件和系统库进行链接形成可执行程序)。gcc/g++就是用来实现这四个步骤的,gdb则是一个调试器,用来debug。

Linux默认生成的可执行程序是动态链接且以release方式发布的!



编译器gcc/g++

gcc用来编译C语言程序,g++用来编译C++程序。它俩的选项基本一致。
语法
gcc/g++ [选项] 要编译的文件 [选项] [目标文件]
常用选项

  • -E 只进行预处理,不生成对应的文件,需要把预处理后的信息重定向到一个输出文件里面(否则将把预处理后的结果打印到屏幕上)。
  • -S 编译到汇编语言,不进行汇编和链接,即只进行预处理和编译。
  • -c 编译到二进制目标代码
  • -o 将处理结果输出到指定文件,该选项后需紧跟输出文件名。
  • -static 此选项对生成的文件采用静态链接。
  • -g 生成调试信息用于debug(若不携带该选项则默认生成release版本)。
  • -shared 此选项将尽量使用动态库,生成文件较小。
  • -w 不生成任何警告信息。
  • Wall 生成所有警告信息。
  • -O0/-O1/-O2/-O3 编译器优化选项的四个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高。

预处理

通过-E选项可以将文件进行预处理,然后通过-o选项将预处理后的信息输出到指定的文件(test.i)中。
预处理阶段的工作主要包括头文件展开、去注释、宏替换、条件编译等。
以test.c文件为例:
gcc -E test.c -o test.i

编译

编译阶段做的工作,首先检查代码的规范性、是否有语法错误(比如变量名错误等,如果是函数名写错,则是在链接阶段检测出来)等,如果有错误会停止并报错,在检查无误后,将代码翻译成汇编语言

与预处理不同的是,即使不加-o选项,也会生成对应的.s文件,但为了规范还是应该加上,以test.i文件编译成test.s为例:
gcc -S test.i -o test.s

汇编

汇编阶段的工作是将编译产生的汇编文件转化为.o二进制目标文件。.o文件就是VS编译器下的.obj文件。
gcc -c test.s -o test.o

由于是二进制文件,所以vim文本编辑器打开以后是乱码:

od 文件名可以查看二进制文件:

链接

链接的主要任务就是将生成的各个.o文件以及库文件进行链接,生成可执行文件。
gcc test.o -o test
链接后生成的也是二进制文件:

gcc/g++不带-E、-S、-c选项时,就默认生成预处理、编译、汇编、链接全过程后的文件。
如果不用-o选项指定生成文件的文件名,则默认生成的可执行文件名为a.out。

所以对于test.c文件想要直接生成可执行程序test,只需要一条命令就行:
gcc test.c -o test

动态库和静态库

对于一个库函数,以printf为例,系统把这些函数实现都被做到名为libc.so.6的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径/usr/lib下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数printf了,而这也就是链接的作用
上面这种方式称为动态链接,依赖动态库

函数库一般分为静态库和动态库两种:

  • 静态库是指编译链接时,把库文件的代码全部加入到可执行文件当中,因此生成的文件比较大,但在运行时也就不再需要库文件了,静态库一般以.a为后缀。
  • 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件当中,而是在程序运行时由链接文件加载库,这样可以节省系统的开销,动态库一般以.so为后缀。gcc 在编译时默认使用动态库

所以,如果在链接的时候出现了链接错误,应该先确认是否存在对应的库。

使用file指令进行查看链接类型,使用ldd指令查看动态链接的可执行文件所依赖的库:

默认采用的是动态链接,但如果我们需要使用静态链接,在后面带上-static选项即可:

静态链接在使用到库函数时,会将库函数的代码拷贝到程序当中,所以程序的体积是比较大的。

不难看出动态链接和静态链接的优缺点:
动态链接
优点:程序的体积比较小,比较节省系统资源(磁盘的空间,内存的空间),bin体积小,加载速度快。
缺点:依赖动态库,程序可移植性较差(一旦库缺失,所有依赖这个库的程序都没法运行了)。
静态链接
优点:不依赖第三方库,程序的可移植性较高(库缺失不影响程序的运行)。
缺点:程序的体积非常大,浪费空间。

在使用静态库之前可能需要先安装,命令如下:
sudo yum install glibc-static
sudo yum install libstdc++-static


调试器gdb

程序的发布方式有两种,debug模式和release模式:

  1. debug版本:程序本身会被加入调试信息,以便于进行调试。因此大小要比release版大。
  2. release版本:不会添加任何调试信息,是不可调试的。

Linux下gcc/g++后的二进制程序,默认是以release方式发布,如果要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g选项以debug方式发布。

使用gdb进行调试
gdb 文件名进入进入调试(文件必须是debug版本)

调试方式和VS2019类似,只不过从图形化界面变成了命令行界面,在gdb中很多命令是可以简写的:

调试

  • run/r:运行代码(启动调试)。
  • next/n:逐过程调试(不进入函数)。
  • step/s:逐语句调试(进入函数)。
  • until 行号:跳转至指定行。
  • finish:执行完当前正在调用的函数后停下来(不能是主函数)。
  • continue/c:运行到下一个断点处。
  • set var 变量=x:修改变量的值为x。
  • 回车:重复上一条指令。

显示

  • list/l n:显示从第n行开始的源代码,每次显示10行,若n未给出则默认从上次的位置往下显示.。
  • list/l 函数名:显示该函数的源代码。
  • print/p 变量:打印变量的值。
  • print/p &变量:打印变量的地址。
  • print/p 表达式:打印表达式的值,通过表达式可以修改变量的值。
  • display 变量:将变量加入常显示(每次停下来都显示它的值)。
  • display &变量:将变量的地址加入常显示。
  • undisplay 编号:取消指定编号变量的常显示。
  • bt:查看各级函数调用及参数。
  • info/i locals:查看当前栈帧当中局部变量的值。

断点

  • break/b n:在第n行设置断点。
  • break/b 函数名:在某函数体内第一行设置断点。
  • info breakpoint/b:查看已打断点信息。
  • delete/d 编号:删除指定编号的断点。
  • disable 编号:禁用指定编号的断点。
  • enable 编号:启用指定编号的断点。

退出gdb

  • quit/q:退出gdb。

补充内容

gcc 默认使用的是 C89 的标准,一些语法是C99才支持的,比如for(int i=0;i<n;i++)中再for循环中定义变量i。因此用C98标准在编译的时候会报错,此时只需要加上-std=c99即可使用C99的标准进行编译:
gcc test.c -o test -std=c99

Ctrl+r可以联想历史输入过的指令。

readelf -S 文件名 | grep debug可以查看程序的调试信息:

以上是关于05gcc/g++和gdb使用的主要内容,如果未能解决你的问题,请参考以下文章

《Linux从0到99》四 Linux编译器(gcc/g++)和调试器(gdb)

《Linux从0到99》四 Linux编译器(gcc/g++)和调试器(gdb)

Linux篇第三篇——Linux环境下的工具(yum + vim + gcc/g++ +gdb)

Linuxgcc/g++/gdb的使用

gcc -g -o -c分别是什么意思

Linux基础开发工具使用(yum,vim,gcc/g++,gdb,make,git)