在Linux中使用VS Code编译调试C++项目

简介: 最近项目需求,需要在Linux下开发C++相关项目,经过一番摸索,简单总结了一下如何通过VS Code进行编译调试的一些注意事项。 关于VS Code在Linux下的安装这里就不提了,不管是CentOS还是Ubuntu,如果不懂且搜Q足够的情况下,你会解决的。

最近项目需求,需要在Linux下开发C++相关项目,经过一番摸索,简单总结了一下如何通过VS Code进行编译调试的一些注意事项。

关于VS Code在Linux下的安装这里就不提了,不管是CentOS还是Ubuntu,如果不懂且搜Q足够的情况下,你会解决的。

一. 前置知识——gcc/g++的编译链接过程

在Windows下,如果你用Visual Studio进行开发,C/C++的编译器一般采用微软提供的MSBuild;在Linux下C/C++的编译器大多采用gcc/g++。既然要在Linux下进行C++开发,很有必要了解一下g++编译器的一些基本知识。

假设我现在有一个最简单的C++文件:

1 #include <iostream>
2 using namespace std;
3 int main() {
4     cout << "Hello, world!!!!" << endl;
5     return 0;
6 }

接下来如何编译呢?简单来说分成两步:先编译,再链接

1. 安装g++编译器

启动终端,进入root模式,安装gcc和g++

Ubuntu:

xxx@xxx:~$ sudo apt-get install build-essential

xxx@xxx:~$ gcc --version
xxx@xxx:~$ sudo apt-get install g++-4.8
xxx@xxx:~$ g++ --version

CentOS:

[xxx@xxx ~]$ su
[xxx@xxx ~]# yum install gcc
[xxx@xxx ~]# gcc --version
[xxx@xxx ~]# yum install gcc-g++
[xxx@xxx ~]# g++ --version

2. 编译hello.cpp

[xxx@xxx ~]$ g++ -c hello.cpp

输出结果是一个hello.o文件,这是编译过程的生成的中间文件。-c 表示只编译,不链接

3. 链接hello.o生成hello.out

[xxx@xxx ~]$ g++ -o hello.out hello.o

输出结果是一个hello.out文件,这是最终的可执行文件。-o 表示输出文件,hello.o是上一步生成的.o文件。

当然,如果第2、3步是可以合并执行,直接执行命令

[xxx@xxx ~]$ g++ -o hello.out hello.cpp

然而第2、3步分开执行是有意义的,后面会讲到。

4. 运行hello.out

最后执行以下hello.out验证一下输出结果呗

[xxx@xxx ~]$ ./hello.out

二. 构建项目

实际开发过程中当然不可能只有一个cpp这么简单,有时候会有非常多的.h和.cpp文件相互配合,那么上面直接通过g++编译可执行文件就没那么简单了。我们需要借助Make这个强大的项目构建工具,帮助我们构建和组织项目代码。

假设现在有如下3个文件:hw2.cpp、solution.h和solution.cpp

1 /* solution.h */
2 class Solution {
3 public:
4     void Say();
5 };
1 /* solution.cpp */
2 #include <iostream>
3 #include "solution.h"
4 void Solution::Say(){
5    std::cout << "HI!" << std::endl;
6 }
复制代码
1 /* hw2.cpp */
2 #include "solution.h"
3 int main () {
4     Solution sln;
5     sln.Say();
6     return 0;
7 }
复制代码

 

可以看到这个简单例子包括头文件引用、定义和实现分离等情况,如果直接g++ -o hw2.out hw2.cpp将会报未定义引用的错误:

[xxx@xxx ~]$ g++ -o hw2.out hw2.cpp 
/tmp/ccIMYTxf.o:在函数‘main’中:
hw2.cpp:(.text+0x10):对‘Solution::Say()’未定义的引用
collect2: 错误:ld 返回 1

 

这时Make就该大显身手了。

首先我们还需要了解一下makefile。

在项目的根目录下创建一个makefile文件,以告诉Make如何编译和链接程序。

复制代码
1 build : hw2.o solution.o
2     g++ -o build hw2.o solution.o #注意前面必须是tab,不能是空格
3 hw2.o : hw2.cpp solution.h
4     g++ -g -c hw2.cpp
5 solution.o : solution.h solution.cpp
6     g++ -g -c solution.cpp
7 clean :
8     rm hw2.o solution.o build
复制代码

 

先来解释一下makefile的基本语法规则:

target ... : prerequisites ...
  command    #注意前面是tab

target是一个目标文件,可以是Object File,也可以是执行文件,还可以是一个标签;

prerequisites是要生成那个target所需要的文件或是目标;

command是make需要执行的命令(任意的Shell命令)。

说白了就是target这一个或多个目标,依赖于prerequisites列表中的文件,其执行规则定义在command里。如果prerequisites列表中文件比target要新,就会执行command,否则就跳过。这就是整个make过程的基本原理。

 

那么,我们回头看看上面定义的makefile文件,我们解释一下每两行的作用

1 build : hw2.o solution.o
2     g++ -o build hw2.o solution.o

target是build,依赖于hw2.o 和 solution.o,执行的命令是 g++ -o build hw2.o solution.o

意思是通过g++链接hw2.o和solution.o,生成可执行文件build,prerequisites有两个.o文件,是因为代码里hw2引用了solution.h。

 

3 hw2.o : hw2.cpp solution.h
4      g++ -g -c hw2.cpp

target是hw2.o,依赖于hw2.cpp和solution.h,执行命令是g++ -g -c hw2.cpp

意思是通过g++编译hw2.cpp文件,生成hw2.o文件,g++命令中 -g 表示生成的文件是可调试的,如果没有-g,调试时无法命中断点。

 

5 solution.o : solution.h solution.cpp
6     g++ -g -c solution.cpp

同上,编译solution.cpp文件,生成solution.o文件。

 

7 clean :
8     rm hw2.o solution.o build

这里clean不是一个可执行文件,也不是一个.o文件,它只不过是一个动作名字,类似于label的作用,make不会去找冒号后的依赖关系,也不会自动执行命令。如果要执行该命令,必须在make后显示指出整个动作的名字,如make clean。

 

好了,接下来说一下make的工作原理。在默认的方式下,我们只需输入make,则发生了以下行为:

a. make在当前目录下找名为makefile或Makefile的文件;

b. 如果找到,它会找文件中的第一个target,如上述文件中的build,并作为终极目标文件;

c. 如果第一个target的文件不存在,或其依赖的.o 文件修改时间要比target这个文件新,则会执行紧接着的command来生成这个target文件;

d. 如果第一个target所依赖的.o文件不存在,则会在makefile文件中找target为.o的依赖,如果找到则执行command,.o的依赖必是.h或.cpp,于是make可以生成 .o 文件了

e. 回溯到b步执行最终目标

 

看一下执行结果

复制代码
[xxx@xxx ~]$ make
g++ -g -c hw2.cpp
g++ -g -c solution.cpp
g++ -o build hw2.o solution.o #注意前面必须是tab,不能是空格
[xxx@xxx ~]$ ./build 
HI!
[xxx@xxx ~]$
复制代码

 

由于makefile文件中加了-g这一选项,于是可以通过gdb进行调试,并且会命中断点,这里感兴趣可以再了解一下gdb的使用。

接下来我们要说到如何通过VS Code进行调试。

 

三. 在VS Code中编译调试

首先安装完VS Code之后,还需要安装一下扩展cpptools,请自行完成。

 

点击菜单 查看-> 调试,或直接快捷键ctrl + shift + D

 

点击设置图标,在弹出的选择环境中选择C++(GDB/LLDB),会自动创建一个launch.json文件

顾名思义,laucn.json的作用是告诉VS Code如何执行启动任务,也就是我们要把什么文件启动起来,在上述例子中显然是build这个可执行文件了。修改一下json文件中波浪线的program节点,改成${workspaceRoot}/build,其余的暂时不变

 

复制代码
 1 {
 2     "version": "0.2.0",
 3     "configurations": [
 4         {
 5             "name": "C++ Launch",
 6             "type": "cppdbg",
 7             "request": "launch",
 8             "program": "${workspaceRoot}/build",
 9             "args": [],
10             "stopAtEntry": false,
11             "cwd": "${workspaceRoot}",
12             "environment": [],
13             "externalConsole": true,
14             "linux": {
15                 "MIMode": "gdb"
16             },
17             "osx": {
18                 "MIMode": "lldb"
19             },
20             "windows": {
21                 "MIMode": "gdb"
22             }
23         },
24         {
25             "name": "C++ Attach",
26             "type": "cppdbg",
27             "request": "attach",
28             "program": "${workspaceRoot}/build",
29             "processId": "${command.pickProcess}",
30             "linux": {
31                 "MIMode": "gdb"
32             },
33             "osx": {
34                 "MIMode": "lldb"
35             },
36             "windows": {
37                 "MIMode": "gdb"
38             }
39         }
40     ]
41 }
复制代码

接着我们尝试一下F5,开始调试,结果可以看到报了一个缺少build文件的错误。原因是我们还没执行make编译出可执行文件呢。我们在launch.json文件中,添加一个preLaunchTask的节点,并设置值为“build”。注意这里的build不是指可执行文件build,而是一个名为build的任务!

复制代码
 1 {
 2     "version": "0.2.0",
 3     "configurations": [
 4         {
 5             "name": "C++ Launch",
 6             "type": "cppdbg",
 7             "request": "launch",
 8             "program": "${workspaceRoot}/build",
 9             "args": [],
10             "stopAtEntry": false,
11             "cwd": "${workspaceRoot}",
12             "environment": [],
13             "externalConsole": true,
14             "preLaunchTask": "build",
15             "linux": {
16                 "MIMode": "gdb"
17             },
18             "osx": {
19                 "MIMode": "lldb"
20             },
21             "windows": {
22                 "MIMode": "gdb"
23             }
24         },
25         {
26             "name": "C++ Attach",
27             "type": "cppdbg",
28             "request": "attach",
29             "program": "${workspaceRoot}/build",
30             "processId": "${command.pickProcess}",
31             "linux": {
32                 "MIMode": "gdb"
33             },
34             "osx": {
35                 "MIMode": "lldb"
36             },
37             "windows": {
38                 "MIMode": "gdb"
39             }
40         }
41     ]
42 }
复制代码

再尝试F5,会提示一个信息:

点击配置任务运行程序,并选择Others, 会自动生成一个tasks.json文件,这个文件的作用就是告诉launch或者编译器需要执行什么操作。显然我们这里要执行make命令,修改tasks.json为如下:

复制代码
 1 {
 2     "version": "0.1.0",
 3     "command": "make",
 4     "showOutput": "always",
 5     "tasks": [
 6         {
 7             "taskName": "clean"
 8         },
 9         {
10             "taskName": "build",
11             "problemMatcher": {
12                 "owner": "cpp",
13                 "fileLocation":  ["relative", "${workspaceRoot}"],
14                 "pattern": {
15                     "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
16                     "file": 1,
17                     "line": 2,
18                     "column": 3,
19                     "severity": 4,
20                     "message": 5
21                 }
22             }
23         }
24     ]
25 }
复制代码

其中tasks节点是一组任务,注意到其中一个名为build的任务,这就是launch.json文件中指定的preLaunchTask,表明在启动可执行程序之前,会先执行一下preLaunchTask即这里的build任务,重新make一下代码,更新可执行程序之后再启动。

当然也可以指运行tasks这些任务而不启动可执行程序,直接ctrl + shift + B,在VSC的console里可以看到和终端执行一样的输出:

执行完后,项目中会多出.o和build文件

 

关于VS Code的launch.json和tasks.json中更多节点的含义,参考

https://code.visualstudio.com/docs/editor/debugging

https://code.visualstudio.com/docs/editor/tasks

 

接着设置好断点之后F5,就可以进入断点调试了

 

 --------------------------------------------------------------------------------------------------------------------------------------------------

 

本篇总结了gcc/g++和make/makefile的基础知识,以及在Linux下使用VS Code进行调试开发的方法,希望对正在挖坑的同学有所帮助,坑避免一个是一个。

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
6月前
|
存储 算法 安全
c++模板进阶操作——非类型模板参数、模板的特化以及模板的分离编译
在 C++ 中,仿函数(Functor)是指重载了函数调用运算符()的对象。仿函数可以像普通函数一样被调用,但它们实际上是对象,可以携带状态并具有更多功能。与普通函数相比,仿函数具有更强的灵活性和可扩展性。仿函数通常通过定义一个包含operator()的类来实现。public:// 重载函数调用运算符Add add;// 创建 Add 类的对象// 使用仿函数return 0;
235 0
|
11月前
|
自然语言处理 编译器 C语言
为什么C/C++编译腰要先完成汇编
C/C++ 编译过程中先生成汇编语言是历史、技术和实践的共同选择。历史上,汇编语言作为成熟的中间表示方式,简化了工具链;技术上,分阶段编译更高效,汇编便于调试和移植;实践中,保留汇编阶段降低了复杂度,增强了可移植性和优化能力。即使在现代编译器中,汇编仍作为重要桥梁,帮助开发者更好地理解和优化代码。
为什么C/C++编译腰要先完成汇编
|
9月前
|
开发工具 git
vscode推送项目到github仓库故障解决1
本文介绍了如何优雅解决本地仓库与远程仓库历史记录不一致的问题,并提供避免未来问题的最佳实践。核心在于理解问题根源(如历史记录差异和常见原因),采用推荐的解决方案(先本地初始化再关联远程仓库),并遵循一致的工作流程、团队协作规范及熟悉 Git 命令。通过强制推送或合并无关历史记录等方式处理现有冲突,同时养成良好习惯以预防类似问题。
|
11月前
|
监控 Linux
Linux systemd 服务启动失败Main process exited, code=exited, status=203/EXEC
通过以上步骤,可以有效解决 systemd 服务启动失败并报错 `Main process exited, code=exited, status=203/EXEC` 的问题。关键在于仔细检查单元文件配置、验证可执行文件的有效性,并通过日志分析具体错误原因。确保可执行文件路径正确、文件具有执行权限,并且可以独立运行,将有助于快速定位和解决问题。
4867 7
|
自然语言处理 编译器 Linux
告别头文件,编译效率提升 42%!C++ Modules 实战解析 | 干货推荐
本文中,阿里云智能集团开发工程师李泽政以 Alinux 为操作环境,讲解模块相比传统头文件有哪些优势,并通过若干个例子,学习如何组织一个 C++ 模块工程并使用模块封装第三方库或是改造现有的项目。
882 56
|
存储 程序员 编译器
简述 C、C++程序编译的内存分配情况
在C和C++程序编译过程中,内存被划分为几个区域进行分配:代码区存储常量和执行指令;全局/静态变量区存放全局变量及静态变量;栈区管理函数参数、局部变量等;堆区则用于动态分配内存,由程序员控制释放,共同支撑着程序运行时的数据存储与处理需求。
620 22
WK
|
机器学习/深度学习 人工智能 算法
那C++适合开发哪些项目
C++ 是一种功能强大、应用广泛的编程语言,适合开发多种类型的项目。它在游戏开发、操作系统、嵌入式系统、科学计算、金融、图形图像处理、数据库管理、网络通信、人工智能、虚拟现实、航空航天等领域都有广泛应用。C++ 以其高性能、内存管理和跨平台兼容性等优势,成为众多开发者的选择。
WK
695 1
|
前端开发 JavaScript 数据库
VSCode编程助手工程能力体验报告(一):通义灵码 - 帮你高效切入新项目、编码和提升质量
我是一位软件工程师,用通义灵码个人版vscode插件的workspace做项目分析和复盘,对比之前没有灵码,现在提效了80%,本文介绍了具体的使用流程。
3517 3
|
Ubuntu Linux 编译器
Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV
通过以上步骤,您已经成功在Ubuntu系统下的VS Code中配置了C/C++项目环境,并能够调用OpenCV库进行开发。请确保每一步都按照您的系统实际情况进行适当调整。
2453 3