2、[P]搭建C++的集成开发环境

搭建C++的集成开发环境

编译(compile)和解释(interpret)

并不是所有的代码都能直接在计算机上运行。计算机的存储是由一串01的存储,所以所有的符号都会变成数字化符号,也就是变成0、1的序列。(正如在科幻电影里表示的那样,很酷 ≧ v ≦)并且计算机的硬件是规定好了的,它们规定了哪些代码能在计算机上运行,哪些不能。这个时候,编程语言的图灵完备性质就显得十分重要。

虽然tsq不愿意这么称,但能在计算机上直接执行的代码被称为二进制代码(binary code)。tsq认为,“二进制”这个词只有在表示自然数的时候才有意义,而不应该说一个代码是二进制的。这种称呼应该是一个历史遗留问题。两个更好的叫法是机器码(machine code)或最终代码(final code)。机器码是一个抽象层,这个抽象层被称为指令集架构(Instruction Set Architecture,ISA)。

我们在写代码的时候,往往写的都是对于人类而言方便阅读的代码,例如C代码和C++代码,但是规定好的计算机硬件只能运行特定的机器码。所以我们有必要进行程序的转换(translation),即将trivial program代码转换为等价的代码,例如硬件可以直接执行的代码。

在现代,translation有两种方式、两种时序和两种发射方式:

两种方式:
编译:以文件为一个整体进行translation,一个文件被称为一个编译单元;
解释:以一行或多行为一个整体进行translation;

两种时序:
预先处理:Ahead Of Execution,AOE;
即时处理:Just In Time,JIT;

两种发射方式:
发射到内存:目标代码保存到内存;
发射到文件:目标代码保存到文件。

执行编译任务的软件被称为编译器(compiler);

执行解释任务的软件被称为解释器(interpreter);

注意:过去的观点认为编译和解释的分水岭是AOE和JIT,例如认为C语言是编译型语言,而python是解释型语言。甚至在很多考题中,编译都被认为是预先处理代码文件,生成可执行二进制代码(发射到文件)的过程;而解释是在运行时读取代码文件,一行一行地转换成可执行代码(发射到内存)并执行的过程。这种传统在如今已然被打破:C语言可以以过程为单位解释执行、可以动态链接、可以即时编译;而JAVA语言是先编译为Java虚拟机规定的代码,然后再由Java虚拟机解释执行,JAVA虚拟机视情况又会编译一些JAVA虚拟机规定的代码到机器码,以加速执行;而python更是可以通过一些工具(如CPython)变成等价的C代码。但是这种传统的方法往往是常规做法(common practice)。由于C++背负了太多的历史遗留,我们不再针对特定的common practice做出评价。虽然可能不被推荐,但是作为代码的编写者,使用common practice往往便于交流和得到帮助。

在所有代码中,除了机器码,有一类代码比较特别,它被称为汇编代码(assembly code)。汇编代码是和某一个机器码有一一对应的关系的代码。我们简称其为as。

把多个编译单元合并为一个编译单元,这个过程称为链接(code linking)。顾名思义,就是把它们像串线一样串起来。

学习C++,你应当记住下列三大编译器的名字,它们在C++的发展历程中起着重要的推动作用:

1. gcc,最广为使用的C/C++/objective-C三个语言的编译器;linux下的常用编译器;
    gcc有各种版本,它的命名规则是<ISA>-[CPU]-<OS>-[ABI]-gcc.其中ISA就是我们介绍的指令集架构,CPU就是计算机硬件中央处理器CPU的名字,OS是操作系统的名字,例如linux和windows。ABI是程序二进制接口,tsq认为这个说法很不恰当,可能是历史遗留问题。作为C++初学者,我们暂时不考虑ABI。 
2. msvc,(microsoft visual c++)微软为windows开发的御用C++编译器;它的可执行文件的名字为cl.exe,简称cl。
3. clang + llvm,一个多语言、跨平台、模块化、开源的编译器框架;
gcc和clang默认是C的编译器,如果要编译C++文件,需要把名字改成g++和clang++。

文本编辑器(editor)

除了编译器之外,程序员会使用一些软件来写代码。我们常常用word来写文章,在bilibili上甚至有将word作为写代码的编辑器的操作。但是大多数用来文字排版的文本编辑器很难胜任写代码的功能,因为它们不但不能准确输入字符串,而且这些花里胡哨还会添麻烦:

首先,文字排版的自动排版可能和代码的格式冲突;

其次,它的自动填充和语法纠错是基于自然语言的,而不是代码的;

我们期望一个文本编辑器能按照我们的想法输入,输入空格就是空格,回车就是回车,不要输入多余的东西,以免破坏代码的语法正确性(一个英文逗号","错打成中文逗号“,”都可能导致一个代码不合法,进而无法编译为机器码!);当然,它最好能提示我们有可能需要什么,同时检测出语法的错误。最好还能方便地在相关代码之间来回跳转,让程序员更加方便定位代码。

这三个功能被称为code formatting、code intellisence和code navigation,是现代文本编辑器的基本功能。

从使用人数上来看,最广为使用的编辑器是VSC(Visual Studio Code),它和VS(Visual Studio)的关系就如同火龙果和火龙的关系一般,尽管它们同样是微软出品。VSC是没有缺点的编辑器。如果有,那么可以安装丰富且强大的插件来弥补。

vscode

其次是被封神的编辑器vim。它是一个集成命令行工具。如果操作系统上没有GUI,只有命令行,大多数程序员往往会选择vim作为文本编辑器。可惜没有GUI。(当然,你可以在VSC上下一个插件假装你在用vim)

再次是emacs,它可以用来吹牛和装X。

nano、gedit是传统(没落贵族)的编辑器,它们正在淡出历史舞台。Visual studio是饱受诟病的编辑器,因为它太慢、太卡了,显得笨重,而且会莫名出现好多奇怪的bug,但是它对小白很友好,只需要点点鼠标就能完成配置和开发(但是熟练的程序员甚至懒得动鼠标,所以VS不受许多资深程序员欢迎,因为它需要动鼠标)。

vsc的成功理所当然实至名归,然而vim封神的道路依然可歌可颂。在历史上程序员派系的大战中,最为激烈的并不是“最优秀的编程语言”,而是“最优秀的文本编辑器”。虽然后者已经以vim的封神为结果落下帷幕,但是emacs和vim的爱恨情仇依然扣人心弦、可歌可颂。如果读者对此感兴趣,可以试着搜索“vim和emacs哪个更好用”。

vim_vs_emacs

调试器(debugger)

全平台统一的,C系列代码主流的调试器的名字是gdb(gnu debugger)。虽然Windows不是gnu,gnu也不是unix,但是这个说法被留传了,成为了一个历史称呼。程序断点和监视点是我们在调试简单C++程序时常用的手段。我们常常会用到更多的手段。用gdb、打断点的方式来寻找程序里的bug,这效率太低了。但它依然很重要,因为它的单步执行常常是程序员理解程序的手段。

gdb

项目构建工具

用来指挥编译器的工具。对于很大的项目,它会有很多的编译单元要处理,所以有必要有个东西来指挥它。常用的项目构建工具是cmake,gnu make,xmake,msvc(没想到吧,又是它)。读者只需要记住cmake 和gnu make的名字即可。

cmake

集成开发环境(Integrated Developing Environment, IDE)

一般认为,IDE = 文本编辑器 + 项目构建工具 + 编译器 + 调试器

常见的C++集成开发环境有Vsual Studio,clion,dev-C++。VSC没有对应的开发环境,因为它只是一个文本编辑器,如果要使用它,我们可以自行配置C++集成开发环境。

实践部分

本实践教程由如下三个目标:

a.安装C++编译器并编译一个C++文件

b.配置一个C++的(集成)开发环境

c.测试C++文件的基本方法

安装编译器

还记得理论部分里面介绍的主流的编译器吗?这次我们来在操作系统上配置一个趁手集成开发环境。当然,如果你执着于特定的编译器,比如clion或者dev-C,而表中没有你想要的编译器,你可以在互联网上寻找对应的教程。

从开发平台上选择一个,tsq推荐用带GUI的linux环境使用C++,例如ubuntu,你会在后面的安装配置阶段体会到windows带来的各种麻烦:

windows
linux (推荐)

如果选择windows,从下面的编译器中选择一个

msvc (推荐)
clang 
mingw

如果选择linux,从下面的编译器中选择一个

gcc (推荐)
clang (推荐)

然后从下面分支里面选择你的分支,安装完毕后,跳到hello world子标题。

linux-gcc

你只需要在命令行下输入以下命令,即可安装gcc、gnu make、gdb全家桶。

    sudo apt install build-essential

或者只安装gcc:

    sudo apt install gcc

如果你的linux上没有apt-get,你可以使用yum来安装这些软件。

安装完毕,在终端上输入

gcc --version

如果gcc打印出了正确的平台信息、版本信息,表明安装成功。

linux-clang

在终端输入下述命令:

    sudo apt install llvm
    sudo apt install llvm-dev

或者,如果你能访问github,你可以按照clang官网上的教程,来从github上的版本仓库中挑选属于你的clang+llvm。

安装完毕,在终端上输入

clang

如果clang打印出了正确的帮助信息,表明安装成功。

windows-msvc && windows-clang

这里面有两个主流派系,一是使用Visual Studio的超懒人派系,它的缺点是你每次打开Visual Studio的时候都会卡顿一会,写代码体验不好,但安装比较容易,设置也比较方便;但是VS有1G还多,加上MSVC足足有4个GB......

二是使用Visual Studio Code的正规军。它的配置要稍微复杂一些,但是功能也更加强大。(tsq强烈推荐)

先打开Visual Studio的下载站,首页会推荐你下载Visual Studio。但我们来这里的目的是下载msvc。

如果你打算使用VS而不是VSC,在下载页面选择和你计算机架构相同的VS的安装包:

tutorial_1

如果你打算使用VSC,向下翻动网页,找到“所有下载-适用于Visual Studio XXXX的工具-Visual Studio XXXX生成工具”,下载这一个安装包。

tutorial_2

无论你选择VS或是VSC,选择安装选项之后,我们会进入到Visual Studio产品的安装页面的引导。这里以Visual Studio Community 2022为例,其他情况类似,选择安装:

tutorial_3

如果你是新手,你可以在“工作负荷”中选择通用C++或windows C++桌面开发,可以参考这篇文章

或者只下载编译器(而不下载调试器和其他支持):不要勾选“工作负荷”中的内容,直接选择“单个组件”,在SDK、库和框架中选择你的windows版本的SDK或者Windows通用C运行时;在编译器、生成工具和运行时中选择你的C++生成工具。(根据你的计算机架构,在ARM生成工具、ARM64生成工具、x86/x64生成工具这三者里面选择正确的选项)。

tutorial_4

tutorial_5

提示:你可以根据你的操作系统版本来判断你的机器架构;如果你是Windows for Mac,你可以选择ARM和ARM64;如果你是Windows的其他发行版,你可以选择x86/64。如果你是Linux,那你应该不需要再次提及这些额外的知识。

此时你的选项中应该是这样(或类似的)的,阅读右侧的安装信息:

如果你选择安装VS,你的项里面应该有这三项:Visual Studio核心编辑器、单个组件里面有一个Windows SDK和MSVC 生成工具;

如果你选择安装VSC,你的项里应该有这两项:Windows SDK和MSVC 生成工具;

tutorial_6

再此基础之上你也可以多勾选几个在学习过程中看上去很华丽但几乎用不到的东西(如果读者决意从事C++开发,将来可能会用到)。

安装完毕,你的开始菜单应该多出了一些开发环境。打开其中任意一个带有Prompt的控制台(而不是命令提示符),输入cl,如果显示的是msvc的帮助信息,而不是command cl not found之类的错误,表明msvc成功安装了。

tutorial_7

tutorial_8

windows-mingw

(真的会有人选择这个选项么?)下载mingW的工具包并解压到一个安全的路径,如C:\mingw\。然后在PATH路径下添加mingw的bin目录到系统PATH或用户PATH。

以下是安装成功的示例。在控制面板-系统与安全中选择“系统”,点击“高级系统设置”,选“高级”,选“环境变量”,选择用户的PATH或者系统的PATH(选择一个即可。这取决于你愿意为你电脑上的多少个用户账户安装mingw),选择“新建”,然后在浏览器中选择你刚才解压到的目录的bin文件夹

install mingw

添加环境变量之后,打开命令提示符,输入gcc -v,如果显示好多好多信息,而不是 command gcc not found,表明安装完成。

hello world

安装成功之后,我们验证一下编译器安装的正确性:

在一个合适的位置新建一个文本文件,并且复制以下代码粘贴至其中:


#include <iostream>

int main()
{
    std::cout << "hello world!" << std::endl;
    return 0;
}

把这个文本文件重命名为“main.cpp”。(windows应该会警告,如果改变扩展名,该文件可能变得不可用,选择确定改变扩展名)

然后在刚才测试过的命令行中依次按照如下顺序输入命令:

如果你的平台是Windows,方才的文本文件的位置为D:\code\main.cpp,你的编译器是msvc(或clang),则你需要输入的指令如下:

D: 

cd D:\code

cl main.cpp

main.exe

msvc可能会根据它的版本来发出警告。

如果你的平台是Linux,方才的文本文件的位置为~\code\main.cpp,你的编译器是gcc(或者clang),则你需要输入的指令如下:


cd ~\code\main.cpp

g++ main.cpp -o main.elf

.\main.elf

如果你的平台是Windows,方才的文本文件的位置为D:\code\main.cpp,你的编译器是mingW,则你需要输入的指令如下:

D: 

cd D:\code

g++ main.cpp -o main.exe

main.exe

如果你在输入main.exe命令后,或者.\main.elf命令后,紧接着就会打印一行“hello world!”,说明你的安装和编译环境都没问题了。

hello world

配置IDE

如果你选择了VS,那么VS作为你的IDE已经可以投入使用了。

如果你选择了VSC,我们还需要做如下工作,因为vscode本身是一个“不带任何杂质的纯文本编辑器”,但很快,你就会因为它的强大能力而折服。

安装简体中文UI

按Ctrl+Shift+X,打开扩展列表。在这里,你可以淘你想要的各种vscode的扩展。在搜索栏中输入Chinese,就会看到简体中文的UI包。安装它即可(安装完毕之后,vscode会提醒你可以更改语言设置,选择change and reset)。

通过使用“Configure Display Language”命令显式设置 VS Code 显示语言,可以替代默认 UI 语言。 按下“Ctrl+Shift+P”组合键以显示“命令面板”,然后键入“display”以筛选并显示“Configure Display Language”命令。按“Enter”,然后会按区域设置显示安装的语言列表,并突出显示当前语言设置。选择另一个“语言”以切换 UI 语言。

(当然,tsq个人建议如果能够使用英文UI,最好尽量使用英文UI,因为者涉及到一些编程术语的翻译问题)

安装C++扩展以支持C++

因为vscode本身是一个“不带任何杂质的纯文本编辑器”,它不能编译C++。按Ctrl+Shift+X,打开扩展列表。在这里,你可以淘你想要的各种vscode的扩展。在搜索栏中输入C++,就会看到C++的包。安装需要的包即可。这里对于小白而言比较推荐微软的C++全家桶:C/C++ Extension Pack。它集成了C/C++的编辑功能,同时还有cmake的工具。

你还想要......

因为vscode本身是一个“不带任何杂质的纯文本编辑器”,它不能支持你的想法。按Ctrl+Shift+X,打开扩展列表。在这里,你可以淘你想要的各种vscode的扩展。在搜索栏中输入你想要的扩展,就会看到你想要的包。安装需要的包即可。

测试C++程序的基本原理

在下一章我们会介绍机器码的流程控制。这里先简单介绍一下。一个进程会从main函数开始运行,从main函数返回结束。如果:main函数返回零值,函数正常结束;
一般来说(具体含义由操作系统决定),如果main函数返回正值,函数预期内的异常结束;如果main函数返回负值,函数预期外的异常结束。如果一个进程崩溃,对外的表现如同main函数返回负值,操作系统可以报告崩溃的状态。

简单来说,一个程序功能正确,是说:

对于正确的输入,这个程序总能够输出正确的输出;
对于正确的输入,这个程序总能够返回0。

所以,我们测试一个C++程序,其实就是测试它的三个方面:

运行时间;
输出;
返回值。

如果你在windows下使用vsc的运行功能,vsc大概率会报错。这是因为单个cpp文件实际上是很难理解它的意义的。以下教程针对vsc,vs的操作类似。(你可能之前使用过VS,正如你在VS中使用的那样,我们要以“项目”为单位构建,才能够调试C++文件)我们新建一个文件夹(有时候我们会说目录而不是文件夹,可以认为它们是同一个意思)作为项目目录,然后在里面新建一个文本文档cmakelists.txt,在里面输入以下内容:


# cmakelists.txt

project(hello)

add_executable(hello hello.cpp)

enable_testing()

add_test(NAME test1 COMMAND hello)

set_tests_properties(test1 PROPERTIES TIMEOUT 1 PASS_REGULAR_EXPRESSION "hello, cpp world!")

在cmake中,project(hello)的意思是,这个项目的名字是hello;

add_executable(hello hello.cpp)的意思是,添加一个hello(.exe)的程序,这个程序由hello.cpp一个编译单元组成;

enable_testing()的意思是,使用ctest测试你的代码。加了这一行代码之后,底部的状态栏会多处一项:run ctest。

add_test(NAME test1 COMMAND hello)的意思是,添加一个名为test1的测试,这个测试是在命令行运行hello;

set_tests_properties(test1 PROPERTIES TIMEOUT 1 PASS_REGULAR_EXPRESSION "hello, cpp world!")的意思是,为test1添加设置,TIMEOUT 1表示代码只允许执行最多1秒;PASS_REGULAR_EXPRESSION "hello, cpp world!"表示我们只检查输出,不检查返回值,如果输出匹配正则表达式"hello, cpp world!",我们就认为这个程序通过了测试。

在测试cmake项目的时候,这些作为测试程序会按批次自动化运行(就像工厂里的流水线那样),测试工程会检测它们的输出和返回值。试想一下,如果你的程序有几千行几万行或是更多的代码,你不会想要一行一行调试它们来验证它们的正确性的!

新建文件,命名为上述的hello.cpp,并把它们放在cmakelists.txt的同一个文件夹(目录)

在hello.cpp中输入以下内容:


#include <iostream>

int main()
{
    std::cout << "hello, cpp world!" << std::endl;
    return 0;
}

底部状态栏(左下)从左到右依次是:

编译诊断报告,0 warnings,0 errors。但是,如果你的vscode如果没有配置默认编译器,那么有可能会报告找不到iostream头文件。这个错误不会影响后面的编译,你可以暂时忽略它。

cmake的构建模式;

编译套件(可以认为是编译器);

开始构建(build);

构建目标[ALL]

将运行目标发送至调试器以调试模式执行。

执行选定的目标;

启动ctest检测项;

select msvc

我们启动ctest,如果你的步骤正确,并且编译器安装正确,那么你会看到ctest出现1/1的标识,表示你的程序通过了1个测试,总共1个测试。

现在你打开hello.cpp,将文件修改为如下:


#include <iostream>

int main()
{
    std::cout << "byebye, cpp world!" << std::endl;
    return 0;
}

再次启动ctest,你会发现ctest变成了0/1。你的vscode底部的“输出”栏详细报告了ctest的运行情况。这是你调试程序bug最强大的利器,好好使用它吧。

windows平台的常见问题

测试你的断点功能

在(底部状态栏的)生成选项中,把版本调成Debug,然后按(底部状态栏的)build构建;接着在你的程序里面打上断点(在行标左侧点出一个红点)。打上断点之后你可以右键编辑它的行为。再把启动目标设置为[hello](如果你没有修改cmake,你会发现你别无选择,这一项只能是hello),然后点击(底部状态栏)的为选择的目标启动调试器(一个虫子图标)。如果程序表现如你所想,表示断点功能正常。

windows配置vscode的C++默认编译选项

如果你的平台是windows,你可能在实践环节发现找不到iostream头文件,也不能在“运行”菜单中选择运行单个的C++文件(当然,运行“单个C++文件”其实也只是一种约定俗成的说法,标准C++规范中没有运行一个C++文件的定义,我们将在下一章讲述怎么组织C++代码,让它是一个合法的C++程序)

在vscode的控制栏(默认是在最左边)中点击管理按钮(是一个齿轮图标!),选择“设置”,或者按[ctrl + ,],进入vscode的配置界面,选择intellisense,在这里你可以调整code intellisense的功能。找到这一栏:

C_Cpp › Default: Compiler Path
compilerPath 未指定或设置为 ${default} 时要在配置中使用的值。

把你的编译器所在目录的地址指定过去即可。例如你使用的是msvc,那就在msvc的安装目录中搜索cl.exe,然后选中名为cl.exe的搜索结果,右击,复制文件地址,粘贴到上述配置中去。现在你的报错应该消失了。回到hello.cpp文件中,按住ctrl,点击iostream,你会发现打开了一个名为iostream的文件。

到此为止,这一章的内容就结束了。你可以回顾章节测试,来检验你的掌握情况。

思考题

1.C++的主流编译器有哪些?用它们编译hello.cpp的命令行是什么?

2.文本编辑器的功能有哪些?如果IDE警告找不到头文件<iostream>,但是代码可以正常编译运行,可能是什么功能出现了问题?

3.请列举两个目前最广为使用的文本编辑器。

4.我们一般怎么称呼C++的通用调试器(general debugger)?

5.C++的项目构建工具有哪些?

6.什么是一个IDE?

7.怎么生成一个程序?怎么调试一个程序?怎么运行一个程序?怎么测试一个程序?

posted @ 2022-12-26 16:28  Tsqurt  阅读(452)  评论(1)    收藏  举报