Linux中使用VS Code编译调试C++项目详解_C 语言

前言

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

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

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

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

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

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

1. 安装g++编译器

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

Ubuntu:

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

 /* solution.h */
 class Solution {
 public:
 void Say();
 };
/* solution.cpp */
 #include <iostream>
 #include "solution.h"
 void Solution::Say(){
 std::cout << "HI!" << std::endl;
 }
 /* hw2.cpp */
 #include "solution.h"
 int main () {
 Solution sln;
 sln.Say();
 return 0;
 }

可以看到这个简单例子包括头文件引用、定义和实现分离等情况,如果直接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如何编译和链接程序。

build : hw2.o solution.o
 g++ -o build hw2.o solution.o #注意前面必须是tab,不能是空格
 hw2.o : hw2.cpp solution.h
 g++ -g -c hw2.cpp
 solution.o : solution.h solution.cpp
 g++ -g -c solution.cpp
 clean :
 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文件,我们解释一下每两行的作用

 build : hw2.o solution.o
 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。

hw2.o : hw2.cpp solution.h
 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,调试时无法命中断点。

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

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

clean :
 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进行调试开发的方法,希望对正在挖坑的同学有所帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索linux
, c
, vs
, 调试
, code
, vscode
编译调试
c语言编译过程详解、codeblocks编译c语言、mac vscode c语言编译、codeblocks 调试、vscode 调试js,以便于您获取更多的相关知识。

时间: 2024-11-03 08:26:09

Linux中使用VS Code编译调试C++项目详解_C 语言的相关文章

基于C++中常见编译错误的总结详解_C 语言

在日常编码过程中会遇见各种编译错误,本文对常见的编译错误进行分析总结.(基本的编译错误在这里不列举,后续后持续更新) 1.error c101008a解决方法该错误出现在项目升级过程中会出现,比如说项目从vs2008升级到vs2010.解决办法:在项目上点右键,清理(Clean),重新编译,问题解决 2.error C2252解决方法该错误主要在项目从vs2008升级到vs2010出先.error C2252: an explicit instantiation of a template ca

浅谈C/C++中的static与extern关键字的使用详解_C 语言

一.C语言中的static关键字在C语言中,static可以用来修饰局部变量,全局变量以及函数.在不同的情况下static的作用不尽相同.(1)修饰局部变量一般情况下,对于局部变量是存放在栈区的,并且局部变量的生命周期在该语句块执行结束时便结束了.但是如果用static进行修饰的话,该变量便存放在静态数据区,其生命周期一直持续到整个程序执行结束.但是在这里要注意的是,虽然用static对局部变量进行修饰过后,其生命周期以及存储空间发生了变化,但是其作用域并没有改变,其仍然是一个局部变量,作用域仅

C++中MFC Tab Control控件的使用详解_C 语言

1. 新建一个MFC工程, 取名MyTab, 选择Dialog based, 然后Finish. 2. 删除对话框上默认添加的三个控件. 添加Tab Control控件并在Property属性中设置ID为IDC_TABTEST 在More Styles里勾上Bottom. 调速尺寸使其布满整个对话框, 我这边Tab Control的尺寸最后为164X203. 在ClassWizard为其添加变量, 变量名为m_tab. 类型为CTabCtrl. 3. 在对话框的初始化函数OnInitDialog

基于C++自动化编译工具的使用详解_C 语言

@echo off  title=HYTradingGateway自动化编译打包工具mode con cols=80 lines=40echo -------------------------------------------------------------echo 使用说明echo.                                                             echo HYTradingGateway自动化编译打包工具.修改echo 使用前提

深入分析C语言中结构体指针的定义与引用详解_C 语言

指向结构体类型变量的使用首先让我们定义结构体:struct stu{char name[20];long number;float score[4];} ;再定义指向结构体类型变量的指针变量:struct stu *p1, *p2 ;定义指针变量p 1.p 2,分别指向结构体类型变量.引用形式为:指针变量→成员:[例7-2] 对指向结构体类型变量的正确使用.输入一个结构体类型变量的成员,并输出. 复制代码 代码如下: #include <stdlib.h> /*使用m a l l o c (

C语言中进程信号集的相关操作函数详解_C 语言

C语言sigismember()函数:测试某个信号是否已加入至信号头文件:#include <signal.h> 定义函数:int sigismember(const sigset_t *set, int signum); 函数说明:sigismember()用来测试参数signum 代表的信号是否已加入至参数set 信号集里. 如果信号集里已有该信号则返回1, 否则返回0. 返回值:信号集已有该信号则返回1, 没有则返回0.如果有错误则返回-1. 错误代码: 1.EFAULT 参数set 指

linux中了minerd之后的完全清理过程(详解)_Linux

一不小心装了一个Redis服务,开了一个全网的默认端口,一开始以为这台服务器没有公网ip,结果发现之后悔之莫及啊 某天发现cpu load高的出奇,发现一个minerd进程 占了大量cpu,google了一下,发现自己中招了 下面就是清理过程 第一步 1.立即停止redis服务,修改端口权限,增加密码措施 2.按照网上的资料 删除 crontab 里的两个内容 sudo rm /var/spool/cron/root sudo rm /var/spool/cron/crontabs/root 3

C#中const 和 readonly 修饰符的用法详解_C#教程

1. 只有C#内置类型(int,double,long等)可以声明为const;结果.类和数组不能声明为const. 2. readonly 是在字段上使用的修饰符,直接以类名.字段访问. 3. const 必须在申明中初始化.之后不能再修改. 4. readonly可以在申明中初始化,也可以在构造函数中初始化,其它情况不能修改. namespace const_and_readonly { class Program { static void Main(string[] args) { Co

linux中cat、more、less命令区别详解

众所周知linux中命令cat.more.less均可用来查看文件内容,主要区别有:cat是一次性显示整个文件的内容,还可以将多个文件连接起来显示,它常与重定向符号配合使用,适用于文件内容少的情况:more和less一般用于显示文件内容超过一屏的内容,并且提供翻页的功能.more比cat强大,提供分页显示的功能,less比more更强大,提供翻页,跳转,查找等命令.而且more和less都支持:用空格显示下一页,按键b显示上一页.下面详细介绍这3个命令. cat   [plain] view p