【Linux下的cpp】编译调试(gcc、g++、gdb)

devtools/2024/11/14 12:54:14/

【Linux下的cpp】编译调试(gcc、g++、gdb)

文章目录

  • 【Linux下的cpp】编译调试(gcc、g++、gdb)
    • 简述gcc、g++、gdb
    • 编译过程
    • g++ 编译参数
    • 命令行编译演练
      • 1、直接编译
      • 2、生成库文件并编译
        • 链接静态库并生成可执行文件
        • 链接动态库生成可执行文件
    • gdb调试器
      • 1. 常用调试命令参数
        • 常用命令演示
      • 2. gdb 切换线程
      • 3. gdb命令行调试演练

不管是从事何种语言开发,合适的IDE以及开发环境是必不可少的。

简述gcc、g++、gdb

相同点

  • GNU 项目:它们都是 GNU 项目的一部分,并且都是开源的。
  • 命令行工具:它们都是命令行工具,适用于 Unix/Linux 环境。
  • 开发工具链:它们都是开发工具链的一部分,用于编译和调试程序。

不同点

  • 功能:

    • gcc:主要用于编译 C 语言程序。
    gcc -o myprogram myprogram.c
    
    • g++:主要用于编译 C++ 语言程序。
    g++ -o myprogram myprogram.cpp
    
    • gdb:用于调试程序,而不是编译。
    gdb myprogram
    
  • 输入和输出:

    • gccg++:接受源代码文件(如 .c.cpp)并生成目标文件或可执行文件。
    • gdb:接受可执行文件,用于调试运行中的程序。

总结

  • gcc 是一个通用编译器,主要用于编译 C 语言代码。
  • g++ 是一个专门的编译器,主要用于编译 C++ 语言代码。
  • gdb 是一个调试器,用于调试 C、C++ 及其他语言编写的程序。

编译过程

编译单个c++文件:g++ test.cpp -o test

单次编译命令

预处理 -》 编译 -》 汇编 -》链接

# 预处理
# -E 指示编译器仅对输入文件进行预处理
# -o  指定输出文件(这里是test.i)
g++ -E test.cpp -o test.i		// 生成.i文件# 编译
# -S 让g++在为C++代码生成汇编语言文件后停止编译
# g++ 产生的汇编语言文件的缺省扩展名是 .s
g++ -S test.i -o test.s# 汇编
# -c 让g++把源代码编译为机器语言的目标代码(只编译,不链接)
g++ -c test.s -o test.o# 链接
# 生成可执行文件
g++ test.o -o test

g++ 编译参数

  1. -g 编译带调试信息的可执行文件
# -g 选项告诉 GCC 产生能被 GNU 调试器GDB使用的调试信息,以调试程序。
# 产生带调试信息的可执行文件test
g++ -g test.cpp -o test
  1. -O[n] 优化源代码
## 所谓优化,例如省略掉代码中从未使用过的变量、直接将常量表达式用结果值代替等等,这些操作会缩减目标文件所包含的代码量,提高最终生成的可执行文件的运行效率。# -O 选项告诉 g++ 对源代码进行基本优化。这些优化在大多数情况下都会使程序执行的更快。 -O2 选项告诉 g++ 产生尽可能小和尽可能快的代码。 如-O2,-O3,-On(n 常为0–3)
# -O 同时减小代码的长度和执行时间,其效果等价于-O1
# -O0 表示不做优化
# -O1 为默认优化
# -O2 除了完成-O1的优化之外,还进行一些额外的调整工作,如指令调整等。
# -O3 则包括循环展开和其他一些与处理特性相关的优化工作。
# 选项将使编译的速度比使用 -O 时慢, 但通常产生的代码执行速度会更快。# 使用 -O2优化源代码,并输出可执行文件
g++ -O2 test.cpp
  1. -l | -L 指定库文件/ 指定库文件路径
# -l参数(小写)就是用来指定程序要链接的库,-l参数紧接着就是库名
# 在/lib和/usr/lib和/usr/local/lib里的库直接用-l参数就能链接# 链接glog库
g++ -lglog test.cpp# 如果库文件没放在上面三个目录里,需要使用-L参数(大写)指定库文件所在目录
# -L参数跟着的是库文件所在的目录名# 链接mytest库,libmytest.so在/home/bing/mytestlibfolder目录下
g++ -L/home/bing/mytestlibfolder -lmytest test.cpp
  1. -I 指定头文件搜索目录
# -I
# /usr/include目录一般是不用指定的,gcc知道去那里找,
#但是如果头文件不在/usr/icnclude里我们就要用-I参数指定了,
# 比如头文件放在/myinclude目录里,那编译命令行就要加上-I/myinclude 参数了,如果不加你会得到一个”xxxx.h: No such file or directory”的错误。
# -I参数可以用相对路径,比如头文件在当前目录,可以用-I.来指定。上面我们提到的–cflags参数就是用来生成-I参数的。g++ -I/myinclude test.cpp
  1. -W 打印警告信息
  2. -w 关闭警告信息
  3. -std=c++11 设置编译标准
  4. -o 指定输出文件名
  5. -c 只编译,不链接
  6. -D 定义宏

image-20240618182132673

命令行编译演练

1、直接编译

# 将 main.cpp src/Swap.cpp 编译为可执行文件
g++ main.cpp src/swap.cpp -Iinclude
# 运行a.out
./a.out

image-20240618210958060

2、生成库文件并编译

注意源文件list 可以放在g++之后,也可以放在命令行最后

链接静态库并生成可执行文件
  1. 简单生成静态库(-c则是编译选项)
    • 语法:g++ -c -o libpublic.a public.cpp
    • 包含头文件则添加-I相对路径
  2. 指定库:
    • -l+指定库名(即libxxxx.a中的xxxx)
    • -L指定库目录(即静态库文件放在哪里)
  3. 链接不带任何限制参数(-o的作用是指定名称,而非链接选项)
# 将需要用到的库编译成机器语言文件
# g++ 源文件 -c(转为机器语言) -I(头文件目录)
# 将生成swap.o
g++ swap.cpp -c -I../include# 生成静态库swap.a(这里的lib是前缀,只要生成静态库都需要这个前缀)
ar rs libswap.a swap.o# 将main.cpp编译为可执行的static_main
# g++ 源文件 -l指定库文件 -L指定库目录 -I指定头文件目录 -o 可执行文件名
g++ main.cpp -lswap -Lsrc -Iinclude -o static_main

image-20240618214558253

链接动态库生成可执行文件
  1. 制作动态库g++ -fPIC -shared -o lib库名.so 源代码文件清单
    • -fPIC -shared:表示制作动态库
  2. 使用动态库需要改变环境变量选项:
    1. echo $LD_LIBRARY_PATH:查看当前库环境变量
    2. export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库所在目录的绝对路径
# 生成动态库libswap.so
g++ swap.cpp -I../include -fPIC -shared -o libswap.so# 生成可执行文件
# g++ 源文件 -I头文件目录 -l库名 -L库目录 -o 指定程序名
g++ main.cpp -Iinclude -lswap -Lsrc -o dynamic_main# 运行方法一,手动指定动态库目录和运行文件
# 执行动态库生成后的可执行文件
# =指定库目录  可执行文件
LD_LIBRARY_PATH=src ./dynamic_main# 运行方法二:将动态库所在目录加入系统的动态库环境变量中
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库所在目录的绝对路径

gdb调试器

gdb调试器是Linux下系统开发C/C++最常用的调试器。

Tips:

  1. 编译程序时需要加上-g,之后才能用gdb进行调试:g++ -g main.cpp -o main

  2. 回车键:重复上一命令

1. 常用调试命令参数

调试开始:执行gdb [exefilename] ,进入gdb调试程序,其中exefilename为要调试的可执行文件名

## 以下命令后括号内为命令的简化使用,比如run(r),直接输入命令 r 就代表命令run$(gdb)help(h)	# 查看命令帮助,具体命令查询在gdb中输入help + 命令$(gdb)run(r)	# 重新开始运行文件(run-text:加载文本文件,run-bin:加载二进制文件)$(gdb)start		# 单步执行,运行程序,停在第一行执行语句$(gdb)list(l) 	# 查看原代码(list-n,从第n行开始查看代码。list+ 函数名:查看具体函数)$(gdb)set		# 设置变量的值,如:1. set args a b c (当main函数带参时); 2. 改变内部变量的值 set var name = "aaa"$(gdb)next(n)   # 单步调试(逐过程,函数直接执行)$(gdb)step(s)	# 单步调试(逐语句:跳入自定义函数内部执行)$(gdb)backtrace(bt)	# 查看函数的调用的栈帧和层级关系$(gdb)frame(f) 	# 切换函数的栈帧$(gdb)info(i) 	# 查看函数内部局部变量的数值$(gdb)finish	# 结束当前函数,返回到函数调用点$(gdb)continue(c)	# 继续运行$(gdb)print(p)	# 打印值及地址$(gdb)quit(q) 	# 退出gdb$(gdb)break+num(b)			# 在第num行设置断点, b20行设置断点$(gdb)info breakpoints		# 查看当前设置的所有断点$(gdb)delete breakpoints num(d)	# 删除第num个断点$(gdb)display				# 追踪查看具体变量值$(gdb)undisplay				# 取消追踪观察变量$(gdb)watch					# 被设置观察点的变量发生修改时,打印显示$(gdb)i watch				# 显示观察点$(gdb)enable breakpoints	# 启用断点$(gdb)disable breakpoints	# 禁用断点$(gdb)x						# 查看内存x/20xw 显示20个单元,16进制,4字节每单元$(gdb)run argv[1] argv[2]	# 调试时命令行传参$(gdb)set follow-fork-mode child#Makefile项目管理:选择跟踪父子进程(fork())
常用命令演示
# 启动调试可执行文件
rdjroot@TM:~/Coding/cppnet$ gdb server# 设置main函数的入参,第一个可执行文件已省略
(gdb) set args 5005# 设置跟踪文件
(gdb) file ~/Coding/cppnet/server
# 展示90行左右的代码
(gdb) list 90# 设置断点在88行
(gdb) b 88# start开始执行,会在main函数前停止,直到continue.
# run会直接执行
(gdb) start# 设置监视变量(公式)
(gdb) display eventfd# 删除监视变量
(gdb) undisplay 1(这里是变量的id或者变量名)# 显示所有的监视变量
(gdb) info display# 退出调试
(gdb) quit

2. gdb 切换线程

常用的 thread 命令示例:

  1. info threads:显示当前程序中所有线程的信息,包括线程号和调用堆栈。

  2. thread <thread-id>:切换到指定线程。您可以使用线程号或者线程索引(从0开始)来切换到相应的线程。

  3. info inferiors:显示所有程序执行实例的信息,每个实例对应一个线程组。

  4. thread apply <thread-id-list> <gdb-command>:对指定线程列表执行命令。例如,thread apply 1 2 3 bt将对线程1、2和3依次执行 bt 命令(打印调用堆栈)。

通过这些命令,您可以方便地管理和调试多线程程序。

3. gdb命令行调试演练

使用gdb命令打开用-g生成的可执行文件,进行调试

image-20240619215604801

4、gdb调试core文件

  1. 修改core限制
# 查看系统限制参数
ulimit -a 
# 修改core问价大小为無限
ulimit -c unlimited
  1. 再次启用运行程序,如果出错会产生core.xxx文件
  2. 调试core文件:gdb core.xxx runexe。->进入gdb命令界面,会显示错误行数
  3. bt查看函数调用栈

5、调试正在运行的程序

  1. 使用命令查看正在执行程序的进程编号:ps -ef | grep run_exe

image-20240701193421469

  1. gdb run_exe -p process_num - > 会使正在运行的程序暂停

修改core限制

# 查看系统限制参数
ulimit -a 
# 修改core问价大小为無限
ulimit -c unlimited
  1. 再次启用运行程序,如果出错会产生core.xxx文件
  2. 调试core文件:gdb core.xxx runexe。->进入gdb命令界面,会显示错误行数
  3. bt查看函数调用栈

5、调试正在运行的程序

  1. 使用命令查看正在执行程序的进程编号:ps -ef | grep run_exe

[外链图片转存中…(img-5Md3n8KT-1726372189697)]

  1. gdb run_exe -p process_num - > 会使正在运行的程序暂停


参考:

[1] 基于VSCode和CMake实现的C/C++开发-Linux篇


http://www.ppmy.cn/devtools/114192.html

相关文章

鸿蒙Harmony应用开发,数据驾驶舱页面的实现

先来看看我们要实现的驾驶舱的页面是什么样的 对于这种 响应式布局的页面构建&#xff0c;我们的脑子里面要有一个概念&#xff0c;就是"分而治之"。我们把这个页面进行分割&#xff0c;分割成不同的块然后再来逐个实现. 不难发现&#xff0c;我们可以将这个看到的效…

json格式互相转换

您提供的字符串已经是一个JSON格式的字符串&#xff0c;但是JSON标准要求键名必须用双引号括起来&#xff0c;而不是单引号。因此&#xff0c;您需要将字符串中的单引号替换为双引号。以下是转换后的JSON字符串&#xff1a; {"图片描述": "高速公路上发生了严重…

基于Spring Boot的学生社区故障维修预约系统的设计与实现(开题报告)

毕业论文(设计)开题报告 基于Spring Boot的学生社区故障维修预约系统设计与实现 姓 名 学 院 数学与数据科学学院 专业班级 信息与计算科学202 学 号 202021314223 校内指导教师 职称/职务 副教授 校外指导教师 职称/职务 技术经理 起始时间 2023年9月 教务部制 一、开…

go语言中的数组指针和指针数组的区别详解

1.介绍 大家知道C语言之所以强大&#xff0c;就是因为c语言支持指针&#xff0c;而且权限特别大&#xff0c;c语言可以对计算机中任何内存的指针进行操作&#xff0c;这样自然而然也会带来一些不安全的因素&#xff0c;所以在golang中&#xff0c;「取消了对指针的一些偏移&…

Day.js时间插件的安装引用与常用方法大全

&#x1f680; 个人简介&#xff1a;某大型国企资深软件研发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

给一个web网站,如何开展测试?

前言 Web测试是指针对Web应用程序(网站或基于Web的系统)进行的测试活动&#xff0c;以确保其质量、性能、安全性、可用性和兼容性等方面符合预期标准。Web测试涵盖了从前端用户界面(UI)到后端逻辑和数据库的各个方面&#xff0c;确保Web应用程序在不同环境和条件下都能正常运行…

【算法】队列与BFS

【ps】本篇有 4 道 leetcode OJ。 目录 一、算法简介 二、相关例题 1&#xff09;N 叉树的层序遍历 .1- 题目解析 .2- 代码编写 2&#xff09;二叉树的锯齿形层序遍历 .1- 题目解析 .2- 代码编写 3&#xff09;二叉树最大宽度 .1- 题目解析 .2- 代码编写 4&#xf…

nanoGPT用红楼梦数据从头训练babyGPT-12.32M实现任意问答

1. 引入 大神karpathy从openai离职后&#xff0c;创办了AI教育公司Eureka Labs&#xff08;参考1&#xff09;&#xff0c;同时也创办了知名的nanoGPT项目。 目前&#xff0c;使用nanoGPT&#xff08;参考2&#xff09;&#xff0c;你可以在几分钟内训练出一个babyGPT&#xf…