在Linux系统下用命令行编译调试C++
目录总结 Ubuntu 下使用 clang 和 lldb 对 C++ 进行编译调试的常用命令。
贴一下系统及工具版本:
mayrain@PC-HOME-Y:~$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.4 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.4 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
mayrain@PC-HOME-Y:~$ cat /proc/version
Linux version 5.15.146.1-microsoft-standard-WSL2 (root@65c757a075e2) (gcc (GCC) 11.2.0, GNU ld (GNU Binutils) 2.37) #1 SMP Thu Jan 11 04:09:03 UTC 2024
mayrain@PC-HOME-Y:~$ clang++ --version
Ubuntu clang version 14.0.0-1ubuntu1.1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
mayrain@PC-HOME-Y:~$ lldb --version
lldb version 14.0.0
一、编译
这里仅介绍常用的单文件及多文件编译,包括第三方库的链接。复杂项目还是直接上IDE哈哈哈。
注意,ubuntu默认是不安装libc++的,我在网上没搜到有什么方便的安装方式,干脆自己编译一下,过程记录如下(参考llvm官网编译指引):
git clone https://github.com/llvm/llvm-project.git ./llvm
cd llvm
mkdir build
cmake -G Ninja -S runtimes -B build -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind"
ninja -C build cxx cxxabi unwind
ninja -C build check-cxx check-cxxabi check-unwind
ninja -C build install-cxx install-cxxabi install-unwind
1. 单文件编译
将源文件 main.cpp 编译为可执行文件 foo:
clang++ -Wall -g -std=c++11 -stdlib=libc++ main.cpp -o foo
2. 多文件编译
首先准备三个文件:
add.h
int add(int a, int b);
add.cpp
#include <iostream>
using namespace std;
int add(int a, int b)
{
int ret = a + b;
cout << "sum of " << a << " and " << b << " is " << ret << endl;
return ret;
}
main.cpp
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include "add.h"
using namespace std;
template <typename T>
void print(const T& v)
{
for (auto iter = v.begin(); iter != v.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
}
int main(int argc, char** argv)
{
cout << "hello lldb!" << endl;
int i = 42;
int *pi = &i;
int i2 = 15;
int i3 = add(i, i2);
string s("A lazy dog jumps over the fox");
string *ps = &s;
vector<int> iv = {0,1,2,3,4,5,6,7,8,9};
set<int> is;
is.insert(0);
is.insert(42);
print(iv);
print(is);
return 0;
}
将以上代码编译为可执行文件 foo:
clang++ -Wall -g -std=c++11 -stdlib=libc++ main.cpp add.cpp -o foo
编译器默认会在当前目录中寻找头文件,所以这里无需单独指定包含目录。
3. 链接第三方动态库
以 opencv 为例:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <string>
using namespace std;
using namespace cv;
int main()
{
string path = "./cv_test.png";
Mat img = imread(path);
namedWindow("cv_test", 0);
resizeWindow("cv_test", 1024, 768);
imshow("cv_test", img);
waitKey(0);
return 0;
}
编译以上示例程序的命令为:
clang++ -Wall -g -std=c++11 -stdlib=libstdc++ -I /usr/local/include/opencv4 -L /usr/local/lib -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs main.cpp -o foo
为什么改成 -stdlib==libstdc++ ?
因为 opencv 库是使用 libstdc++ 进行编译的,如果两者不一致,会报cv::imread
函数找不到定义。细节可以看这篇文章
二、调试
本文仅列举常用调试命令,遇到不清楚的用法可以使用以下命令看说明:
lldb --help
也可以在lldb
交互命令行中使用help [命令]
获取某个具体命令的帮助信息:
以下内容用本文三.2节的三个源文件作为调试demo。
1. 启动和退出
两种启动方式:
退出也有两种方式:
exit
或quit
2. 查看源代码:list/l
查看从给定行号开始的10行代码:
list [行号]
或l [行号]
按回车可以显示下边10行
查看函数代码:
l [函数名]
3. 断点:breakpoint/br、watchpoint
在函数起始处设置断点:
br set --name [函数名]
设置完断点,run
运行foo即可
在指定行设置断点:
br set --line [行号]
因为此时程序还断在main函数的开头,所以输入continue
或c
让程序继续
在指定文件的指定行设置断点:
br set --file [文件名] --line [行号]
查看断点列表:
br list
删除某个/多个断点:
br delete [断点序号1] [断点序号2] [断点序号3]
br delete [n-m]
删除所有断点:
br delete
启用/禁用断点:
br enable [序号]
br disable [序号]
内存断点:watchpoint/watch(当内存发生读/写操作时触发)
常用的条件断点:
watch set var [变量名]
watch modify -c '([变量名]==[值])'
删除、启用/禁用操作和breakpoint类似。
4. 单步、步入、跳出
单步 Step-over:
next
或n
步入 Step-in:
step
或s
跳出 Step-out:
finish
操作截图:
在add
函数调用行设置并触发断点
步入add
单步
跳出add
,可以看到lldb会把返回值列出来
修改函数返回值:
thread return [表达式]
5. 计算表达式命令:expression/expr、p、po
p
是expr --
的简写,po
是expr -o --
的简写,它们会把参数编译并打印输出,两者仅是格式不同。
查看变量的值:
p [变量名]
或po [变量名]
指定输出格式/做进制转换:
p/[格式] [变量名]
修改变量值:
expr [变量名]=[值]
用frame variable
查看变量前后变化
6. 操作内存:memory read/x、memory write
读内存:
x/[读取个数][字节数][格式] [内存地址]
[读取个数]
:整数
[字节数]
:b-1Byte、h-2Byte、w-4Byte、g-8Byte
[格式]
:x-16进制、f-浮点数、d-10进制
写内存:
memory write [内存地址] [值]
7. 获取当前线程调用栈:thread backtrace/bt
8. 一些进阶操作
显示汇编代码:disassemble/d
d -b
d --frame
d --name [函数名]
d -a [内存地址]
d -s [内存地址]
寄存器操作:register read、register write
标签:--,下用,C++,编译,c++,br,Linux,include,断点 From: https://www.cnblogs.com/jy5380/p/18166743