系统环境:Ubuntu18.04 虚拟机
开发语言:C++
开发IDE :VSCode
0 前言
本篇讲解会是一个循序渐进的过程,前面4个部分的内容都是在为最后讲解 VSCode 开发 C++ 而做的铺垫。本文主旨是介绍如何在 Linux 系统下使用辅助工具快速开发 C++ 程序,不会涉及 C++ 语言本身语法的讲解。
通过阅读本文,你将学会如下技能:
1、Linux 下 C++ 开发环境搭建。
2、g++ 编译器语法。
3、使用 GDB 调试程序。
4、使用 CMake 构建工程。
5、使用 VSCode 进行 C++ 开发和调试。
1 开发环境搭建
1.1 编译器,调试器安装。
1 | sudo apt update |
1.2 安装成功确认。
1 | # 以下命令确认每个软件是否安装成功 |
2 GCC编译器
GCC 英文全称:GNU Compiler Collection,它可以编译 C、C++、JAV、Fortran、Pascal、Object-C、Ada 等语言,VSCode 就是通过调用 GCC 编译器来实现 C/C++ 的编译工作的。
gcc 和 g++ 都是 GCC 下的编译器,实际开发中,我们把 gcc 当成c语言编译器,g++ 当成 c++ 语言编译器使用即可。
如果要你了解更多有关gcc与g++的区别,请看下面这篇文章:
https://www.cnblogs.com/liuzhenbo/p/11027197.html
本文针对的开发语言是 C++ 语言,所以接下来本小节将介绍 C++ 编译器 g++ 的使用技巧。
2.1 编译过程。
1 | //test.cpp |
以编译 test.cpp 文件为例,下面是使用 g++ 编译器生成可执行文件的完整步骤:
1、预处理。
1 | g++ -E test.cpp -o test.i |
在当前目录下会多出一个预处理结果文件 test.i,打开 test.i 可以看到,在 test.c 的基础上把 iostream 的内容插进去了。
2、编译为汇编代码。
1 | g++ -S test.i -o test.s |
-S 参数告诉 g++ 在为 C++ 代码产生了汇编文件后停止编译,-o 为指定文件名。
3、汇编为目标文件。
1 | g++ -c test.s -o test.o |
test.o 就是目标文件。目标文件与可执行文件类似,都是机器能够识别的可执行代码,但是由于还没有链接,结构会稍有不同。
4、链接并生成可执行文件。
1 | g++ test.o -o test |
实际上,以上4个步骤所做的工作可以通过一条命令搞定:
1 | g++ test.cpp -o test |
2.2 g++重要编译参数。
1、-g:告诉 g++ 产生能被 GDB 调试器使用的调试信息,以调试程序。
2、-O[n]:优化源代码。例如省略掉代码中从未使用过的变量、直接将常量表达式用结果值代替等,这些操作会缩减目标文件所包含的代码量,提高最终生成的可执行文件的运行效率。
-O 同时减小代码的长度和执行时间,其效果等价于-O1。
-O0 表示不做优化。
-O1 为默认优化。
-O2 除了完成-O1的优化之外,还进行一些额外的调整工作,如指令调整等。
-O3 则包括循环展开和其他一些与处理特性相关的优化工作。
使用-O选项后将使编译的速度变慢,但通常产生的代码执行速度会更快。
3、-l 和 -L:-l 指定库文件, -L 指定库文件路径。
-l 就是用来指定程序要链接的库,-l 参数紧接着就是库名。在 /lib 和 /usr/lib 和 /usr/local/lib 里的库直接用 -l 参数就能链接。例如要链接 glog 库:
1 | g++ -lglog test.cpp |
如果库文件没放在上面三个目录里,需要使用 -L 参数指定库文件所在目录。-L 参数跟着的是库文件所在的目录名。
例如要链接 mytest 库,而 libmytest.so 在 /home/txm/mytestlibfolder 目录下,就可以这样写:
1 | g++ -L/home/txm/mytestlibfolder -lmytest test.cpp |
4、-I:指定头文件搜索目录。
/usr/include 目录一般是不用指定的,g++ 知道去那里找,但是如果头文件不在 /usr/include里我们就要用 -I 参数指定了,比如头文件放在 /myinclude 目录里,那编译命令行就要加上 -I/myinclude 参数了,如果不加你会得到一个”xxxx.h: No such file or directory”的错误。
1 | g++ -I/myinclude test.cpp |
-I 参数也可以用相对路径,比如头文件在当前目录,可以用 -I. 来指定。
5、 -Wall:打印警告信息。
6、-w:关闭警告信息。
7、 -std:设置编译标准。
1 | #使用 c++11 标准编译 test.cpp |
8、-o:指定输出文件名。
1 | # 指定输出可执行文件名为test |
9、-D:定义宏。
-DDEBUG 定义了 DEBUG 宏,可能文件中有 DEBUG 宏部分的相关信息,用个 DDEBUG 来选择开启或关闭 DEBUG。示例代码:
1 | #include |
注:使用man g++命令可以查看 gcc 英文使用手册,见下图。
2.3 g++命令行编译实战。
2.3.1 直接编译。
最简单的编译,并运行:
1 | # 将 main.cpp src/Swap.cpp 编译为可执行文件 |
增加参数编译,并运行:
1 | # 将 main.cpp src/Swap.cpp 编译为可执行文件 附带一堆参数 |
2.3.2 生成库文件并编译。
生成静态库、链接生成可执行文件:
1 | ## 进入src目录下 |
生成动态库、链接生成可执行文件:
1 | ## 进入src目录下 |
3 GDB调试器
GDB(GNU Debugger) 是一个用来调试 C/C++ 程序的功能强大的调试器,是 Linux 系统开发 C/C++ 最常用的调试器,VSCode 就是通过调用它来实现 C/C++ 的调试工作的。
执行gdb [filename] ,即可进入 gdb 调试程序。下面是常用的 GDB 调试命令:
1 | # 以下命令后括号内为命令的简化使用,比如run(r),直接输入命令 r 就代表命令run。 |
Tips:
1、编译程序时需要加上 -g,之后才能用 gdb 进行调试:g++ -g main.c -o main
2、回车键:重复上一命令。
下面给出一段C++代码,供上机练习调试命令:
1 | #include |
4 使用CMake构建
CMake 是一个跨平台的安装编译工具,可以用简单的语句描述所有平台的安装编译过程。
关于 CMake 与 GCC、Make、Makefile、CMakelists 这几个概念之间的联系,请参看这篇文章:http://www.360doc.com/content/19/0906/11/19244573_859450857.shtml
需要纠正一点,文章中说 “CMakelists 的存在是为了避免在各个目录下写 Makefile”,这句表述是不准确的。正确的说法应该是 “CMakelists 的存在是为了避免在跨平台开发时手动编写针对各个平台的 MakeFile 文件和工作空间”,可以参考 CMake官网介绍。
4.1 语法特性及使用步骤。
基本语法格式:指令(参数 1 参数 2…)
- 参数使用括弧括起。
- 参数之间使用空格或分号分开 。
- 指令是大小写无关的,参数和变量是大小写相关的 。
- 变量使用${}方式取值。
在 linux 平台下使用 CMake 构建 C/C++ 工程的流程如下:
- 手动编写 CMakeLists.txt。
- 执行命令 cmake PATH 生成 Makefile ( PATH 是顶层CMakeLists.txt 所在的目录 )。
- 执行命令 make 进行编译。
4.2 两种构建方式。
4.2.1 内部构建(in-source build):不推荐使用。
内部构建会在同级目录下产生一大堆中间文件,这些中间文件并不是我们最终所需要的,和工程源文件放在一起会显得杂乱无章。
1 | # 在当前目录下,编译本目录的CMakeLists.txt,生成Makefile和其他文件 |
4.2.2 外部构建(out-of-source build):推荐使用。
将编译输出文件与源文件放到不同目录中。
1 | # 1. 在当前目录下,创建build文件夹 |
4.3 CMake重要指令和常用变量。
4.3.1 重要指令。
1、cmake_minimum_required:指定 CMake 的最小版本要求。
1 | # 语法:cmake_minimum_required(VERSION versionNumber) |
2、project :定义工程名称,并可指定工程支持的语言。
1 | # 语法:project(projectname [CXX] [C] [Java]) |
3、set:显式的定义变量。
1 | # 语法:set(VAR [VALUE]) |
4、include_directories:向工程添加多个特定的头文件搜索路径,相当于指定 g++ 编译器的 -I 参数。
1 | # 语法:include_directories(dir1 dir2 ...) |
5、link_directories:向工程添加多个特定的库文件搜索路径,相当于指定 g++ 编译器的 -L 参数。
1 | # 语法:link_directories(dir1 dir2 ...) |
6、add_library:生成库文件。
1 | # 语法:add_library(libname [SHARED|STATIC|MODULE] source1 source2 ...) |
7、add_compile_options:添加编译参数。
1 | # 添加编译参数 -Wall -std=c++11 -O2 |
8、add_executable:生成可执行文件。
1 | # 语法:add_executable(exename source1 source2 ...) |
9、target_link_libraries:为 target 添加需要链接的共享库,相当于指定 g++ 编译器 -l 参数。
1 | # 语法:target_link_libraries(target library1 library2 ...) |
10、aux_source_directory :发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表。
1 | # 语法:aux_source_directory(dir VARIABLE) |
11、target_include_directories:指定编译目标文件使用的头文件所在目录。
1 | # 语法:target_include_directories(target <INTERFACE|PUBLIC|PRIVATE> [items1...][<INTERFACE|PUBLIC|PRIVATE> [items2...] ...]) |
12、install:指定执行make install时的规则。
1 | # 语法1:install(TARGETS files... DESTINATION dir) |
4.3.2 CMake常用变量。
1、CMAKE_C_FLAGS:gcc 编译选项。
2、CMAKE_CXX_FLAGS:g++ 编译选项。
1 | # 在CMAKE_CXX_FLAGS编译选项后追加-std=c++11 |
3、CMAKE_BUILD_TYPE:编译类型(Debug, Release)。
1 | # 设定编译类型为debug,调试时需要选择debug,此时就可以用GDB调试了。 |
4、CMAKE_BINARY_DIR|PROJECT_BINARY_DIR|_BINARY_DIR:这三个变量指代的内容是一致的,如果是内部构建,指的是工程顶层目录。如果是外部构建,指的就是工程编译发生的目录。
5、CMAKE_SOURCE_DIR|PROJECT_SOURCE_DIR|_SOURCE_DIR:这三个变量指代的内容是一致的,不论采用何种构建方式,都是工程顶层目录。
6、CMAKE_C_COMPILER:指定C编译器。
7、CMAKE_CXX_COMPILER:指定C++编译器。
8、EXECUTABLE_OUTPUT_PATH:可执行文件输出的存放路径。
9、LIBRARY_OUTPUT_PATH:库文件输出的存放路径。
4.4 代码实践。
本部分针对两个小项目来写对应的 CMakeLists.txt。
4.4.1 最小CMake工程。
1 | # Set the minimum version of CMake that can be used |
4.4.2 多目录工程 - 直接编译。
1 | # Set the minimum version of CMake that can be used |
4.4.3 多目录工程 - 生成库编译。
1 | # Set the minimum version of CMake that can be used |
5 使用VSCode进行C++开发和调试
5.1 VSCode高频使用的快捷键。
| 功能 | 快捷键 | 功能 | 快捷键 |
|---|---|---|---|
| 转到文件 / 其他常用操作 | Ctrl + P | 关闭当前文件 | Ctrl + W |
| 打开命令面板 | Ctrl + Shift + P | 当前行上移/下移 | Alt + Up/Down |
| 打开终端 | Ctrl +` | 变量统一重命名 | F2 |
| 关闭侧边栏 | Ctrl + B | 转到定义处 | F12 |
| 复制文本 | Ctrl+C | 粘贴文本 | Ctrl+V |
| 保存文件 | Ctrl+S | 撤销操作 | Ctrl+Z |
在 Ctrl+P 窗口下还可以:
- 直接输入文件名,跳转到文件。
- ? 列出当前可执行的动作。
- : 跳转到行数,也可以 Ctrl+G 直接进入。
- @ 根据分类跳转 symbol ,查找属性或函数。
- # 根据名字查找 symbol ,也可以 Ctrl+T。
5.2 插件准备。
使用 VSCode 开发 C/C++ 需要安装以下三款插件:
1、C/C++
2、CMake
3、CMake Tools
5.3 开发与调试。
我们在VSCode IDE编写一个具有数值交换与打印功能的工程,下面是项目的结构:
include/swap.h
1 | #pragma once |
src/swap.cpp
1 | #include "swap.h" |
main.cpp
1 | #include "swap.h" |
CMakeLists.txt
1 | # Set the minimum version of CMake that can be used |
使用外部构建方式编译工程,在工程根目录下依次执行如下命令:
1 | mkdir build |
之后会在 build/bin 目录下生成swap_cmake可执行文件,运行效果如下:
现在我们要使用 VSCode 自带调试工具调试该工程,按照如下步骤新建launch.json文件:
修改调试对象:
Tips:
实际上,你也发现了 VSCode 的调试工具还是使用的 GDB 调试器。
F5启动调试:
可以调试了!!!!
但是,如果每次要调试程序都要手动执行一下构建命令,那么就太麻烦了。如何做到每次修改程序后,直接进入调试流程呢?
只需在launch.json文件的同级目录下添加一个task.json文件:
1 | { |
然后修改launch.json文件中 preLaunchTask 值与task.json文件中 label 值保持一致:
这时候修改代码并按 F5 调试,就能立马看到代码修改后的运行结果了!!!
结束语
虽然本文是针对 C++ 的开发指南,但对于 Linux 系统下的 C 开发也类似,实际开发遇到问题时需要变通一下。
本文主要参考的是一个B站视频,笔者去除了视频中的一些简单知识点并保留了关键部分,而后还参考了一些其它网络资料。本文还没有涵盖所有相关的知识点,以后在开发过程中遇到再来补充。
Bilibili视频地址:https://www.bilibili.com/video/BV1fy4y1b7TC?p=1
其它参考过的资料:
https://cmake.org/
https://www.cnblogs.com/liuzhenbo/p/11027197.html
http://www.360doc.com/content/19/0906/11/19244573_859450857.shtml
https://zhuanlan.zhihu.com/p/55207498
https://juejin.cn/post/6844904013696073742