阅读前须知

本文讲述了命令行,环境变量,vscode 配置调试功能,这些对于新生理解都较为困难。不过对于新生都是些有趣的新玩意,尽力理解和尝试,也可以让你对程序的了解更进一步。恭喜开始见识到神秘的计算机…
看不太懂也很正常,反正去年我也看不懂(偷笑

Missing Class Lec.1

0x11 在命令行中调用程序

找到键盘上的 shift 键,按住,右键点击桌面,选择 在此处打开 PowerShell 窗口 ,输入 explorer 并按下回车,你会发现弹出了一个文件资源管理器窗口。这看起来很神奇,不是吗?

几乎所有的程序都可以通过命令行调用,通常我们以 命令 + 参数(可能没有或有很多) 的形式调用程序。就像这样 command arg1 arg2 ... argN

command 指你想要调用的程序,或者说命令也行;arg 指传递给程序的参数。

在命令行中调用程序有点类似于在 C 语言中调用函数,一个程序可以支持一个或多个参数。

举个具体的例子。这里介绍一下 cat (concatenate)命令。cat 命令接受一个文件或多个文件路径作为参数,并将这些文件的内容按顺序输出。

注意,Windows 的 cmd 不支持 cat 命令,但是 PowerShell 支持。

假设我们在桌面上新建一个 hello.txt ,内容如下:

1
2
3
4
// hello.txt
// 上面一行表示这个代码块是hello.txt的内容
// 自己尝试的时候就不要把这一行也放到hello.txt里啦
Hello world!

这时候按住 shift并右键点击桌面,选择 在此处打开 PowerShell 窗口 ,输入 cat .\hello.txt

1
2
3
4
5
# PS C:\Users\Admin\Desktop> 的作用是提示你当前在桌面目录下
PS C:\Users\Admin\Desktop>cat .\hello.txt

# 你会得到如下输出
Hello world

这时候会有一个问题:程序如何接收来自命令行的参数?

在学习 C 语言的时候你可能见过 main 函数接收命令行参数的参数列表(没见过也没关系,往下看)

1
int main(int argc, char** argv)	// 第二个参数也可写作 char* argv[]

main 函数接收两个参数,

一个是 argc ,意思是 argument count,表示传递给程序的参数数量。

一个是 argv ,意思是 argument vector,是一个字符指针数组(可以认为是二维字符数组),每个元素(每一个指针指向的字符串)都对应命令行传递的一个参数。

尝试运行下面这个程序

1
2
3
4
5
6
7
8
#include <stdio.h>

int main(const int argc, char* argv[]) {
printf("Argument Count : %d\n", argc);
for (int i = 0; i < argc; ++i)
printf("%s", argv[i]);
return 0;
}

你可能得到类似如下结果,其中,第二行是你的程序的绝对路径。

1
2
3
4
Argument Count : 1
C:\User\Admin\Workspace\program.exe

// 不难注意到,argc 一定不少于 1,argv[0] 一定是你的程序的绝对路径

C / C++ 程序可以通过 main 函数参数的方式接收来自命令行的参数。

基于其他语言编写的程序也会有类似的功能,在程序入口处接收命令行传递的参数,在此不做展开。

练习

接下来请你稍加思索,使用上面介绍的接收命令行参数的方式,实现一个 cat 程序。

要求是可以通过类似 .\cat.exe .\hello.txt 的形式读取指定文件的内容并输出到控制台。

文档最后有答案,但是自己先想想怎么写。

0x12 环境变量和 $PATH

上一节我们学习了如何在命令行中调用一个程序,这节我们来介绍环境变量和 $PATH

环境变量是操作系统用来存储有关系统和用户会话信息的变量。这些变量可以包含文件搜索路径、临时文件目录、应用程序特定选项等信息。(大概知道就行,不知道也行)

PATHPATH 中搜索程序。这样做有一个好处:我们只需要关心我们调用的程序叫啥而不必关心它的具体位置。换句话说,不论你当前所在的目录是什么,都可以直接调用环境变量中的程序。

Windows 下按 Win + s 输入 env 即可查看和编辑环境变量

获取一个 GCC(G++) 并添加到环境变量

去下载一个 CLionzip 包,打开它,在 bin 目录下找到 mingwgdb,把这两个目录解压出来放到一个自己的文件夹里,比如 C:\Software\mingw64,此时文件结构如下 。

1
2
3
4
5
6
# 注意如果自己定义路径,不要有特殊符号和中文
C:
|__Software # 自己建的文件夹
|__mingw64 # 同上
|__mingw # 从CLion压缩包拿来的
|__gdb # 同上

gcc.exeg++.exemingw 内部的 bin 目录下

mingw 里的 bin 目录加入环境变量,步骤如下:

  • 首先找到 mingwbin 目录(里面有 gcc.exeg++.exe 的就是)
  • 然后复制这个路径,按 Win + s 输入 env 打开环境变量设置
  • 找到 “用户的环境变量” 一栏下的 Path ,双击,新增一条,把复制的路径粘贴进去
  • 点击 Path 弹出的窗口右下角的确定,再点击环境变量窗口右下角的确定。(有一个不点确定都不会保存)

验证

在桌面打开一个新的 PowerShell 窗口(方法前文有提到),输入 gcc -v 或者 g++ -v 。如果在输出内容的最后一行看到类似如下内容,则说明你的环境变量配置成功了。

1
gcc version 13.1.0 (GCC)

想要查询环境变量中已经存在的 g++ 的位置,可以使用

1
where.exe g++    // .exe不能省略

现在假设我们把 C++ 的编译器 g++.exe 存储在 C:\mingw\bin\g++.exe

我们编译当前目录下的一个简单的程序使用的命令如下

第一个参数是你的 .cpp 文件;第二个参数是 -o ;第三个是你想要输出的文件名,后缀是 .exe 就行

1
g++.exe .\myProgram.cpp -o .\myProgram.exe

如果我们没有把 g++ 添加到环境变量,那么想要编译当前目录下的 main.cpp ,需要执行如下命令

注意到我们需要打出完整路径(路径可以用 Tab 补全),但还是十分甚至九分的麻烦

1
C:\mingw64\bin\g++.exe .\main.cpp -o main.exe

将一些程序添加到 PATH便使PATH 里寻找它们依赖的工具。

0x13 在 Windows 下使用 VS Code 配置 C++ 调试环境

Why VS Code ? B 站 绿导师

下载并安装 vs code 之后记得在扩展里搜索 Simplified Chinese 并下载以启用简体中文。

请确保你已经将 g++ 添加到环境变量

创建一个 VS Code 工作区

VS Code 将一个文件夹视作一个工作区。所以需要创建一个文件夹(路径不要有奇怪的符号和中文)然后用 VS Code 打开,作为我们的工作区。

在工作区下创建 .vscode 文件夹,在 .vscode 文件夹创建 launch.jsonsettings.json ,用于我们对此工作区的配置。文件目录结构大致如下

1
2
3
4
5
Workspace // 工作区文件夹
|__ .vscode
|__ launch.json
|__ settings.json
|__ your-code.cpp

使用 Code Runner 插件

下载 Code Runner 插件,使用 Run Code (或者快捷键 Ctrl + Alt + N )就已经可以编译运行 C++ 代码了。

Code Runner 本质是在调用环境变量里面的 g++ 编译你的代码,然后运行。它默认会为你执行 cd $dir && g++ $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt ,其中 $dir $fileName $fileNameWithoutExt 分别表示文件所在目录、文件名、不包含扩展名的文件名。

建议在工作区的 settings.json 并添加如下设置,让 Code Runner 每次执行前都先保存你的代码,避免总是需要手动保存的麻烦。(欸我明明改了代码为什么编译出来的程序没变呀?)

1
2
3
4
5
6
// settings.json

{
// ...其他的设置
"code-runner.saveAllFilesBeforeRun": true
}

正式赛场环境通常都会提供 VS Code 和 Code Runner ✌

使用 C/C++ 插件

VS Code 的 C/C++ 插件如果只是编译运行单个 .cpp 文件其实并没有明显的优势,但是它提供了傻瓜式的调试功能,这好。

假设此时你已下载了 C/C++ 插件,按下 ctrl + shift + p ,输入 build ,选择 任务:配置默认生成任务 ,然后选择 C/C++: g++.exe 生成活动文件,此时会在 .vscode 文件夹下生成 tasks.json,内容大致如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// tasks.json
{
"version" : "2.0.0", // 如果这条没有生成就自己补上
"tasks" : [
"type": "cppbuild",
// label是这个任务的名称,在后面的launch.json的配置中对应 "preLaunchTask" 的值
"label": "C/C++: g++.exe 生成活动文件",
// g++的路径
"command": "C:\\Software\\mingw64\\bin\\g++.exe",
// 传递给 g++ 的参数
"args": [
"-fdiagnostics-color=always",
// -g 表示编译时生成调试信息
// 如果生成的程序不包含调试信息,是无法使用gdb调试的
"-g",
"${file}",
"-o",
"${fileDirname}\\${fileBasenameNoExtension}.exe"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "编译器: C:\\Software\\mingw64\\bin\\g++.exe"
]
}

然后打开 .vscode 目录下的 launch.json (如果不存在就自己创建一个),你会注意到右下角有一个 添加配置... 的按钮。点击它,选择 (gdb) 启动 ,此时会生成类似如下内容。

有三处内容需要自己手动修改,如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// launch.json
{
"version": "2.0.0",
"configurations": [
{
"name": "(gdb) 启动",
"type": "cppdbg",
"request": "launch",

// 加入一行preLaunchTask,表示在启动程序之前先执行tasks.json里写的编译程序的任务
// 这里冒号右边写前文 tasks.json 定义的任务label(C/C++: g++.exe 生成活动文件)
// 如果觉得有中文很奇怪也可以改成自己想要的label,记得tasks.json和launch.json都要改
"preLaunchTask": "C/C++: g++.exe 生成活动文件",

// 这里表示你启动的程序名,如果按照上文的tasks.json进行编译,则应当修改为
"program": "${workspaceFolder}\\${fileBasenameNoExtension}.exe"
//
// 默认这里给你写的是 "program": "输入程序名称,例如 ${workspaceFolder}/a.exe",

"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",

// 这里需要手动指定gdb的路径,在mingw下的bin中
// 可以按照前文方式打开powershell运行where.exe g++
// 然后在显示的路径中找到mingw文件夹的路径
// 把路径复制到文件资源管理器中打开,找到mingw下的bin文件夹里的gdb.exe
// 如果按照前文方式进行配置,应当是
"miDebuggerPath": "C:\\Software\\mingw64\\bin\\gdb.exe",
// 默认这里给你写的是 "miDebuggerPath": "/path/to/gdb",

"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "将反汇编风格设置为 Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
]
}

至此,你已经完成了 VS Code 下简单的 C++ 编译和调试环境的搭建 🎉

此时工作区的文件结构如下

1
2
3
4
5
6
Workspace
|__.vscode
|__launch.json
|__tasks.json
|__settings.json
|__your_code.cpp

在你的代码中按 f9 可以添加断点,在有断点的情况下按 f5 启动程序可以进行调试。

TODO 拓展

GCC,Clang,MSVC

卡常、O2、条件编译、文件读入

通过预编译提升本地编译速度

TODO 0x14 在 ICPC 现场赛使用 ICPC-Ubuntu

ICPC Image Installation

练习答案

0x11 Cat 程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>

int main(const int argc, const char* argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
return 1;
}

for (int i = 1; i < argc; i++) {
FILE* file = fopen(argv[i], "r");
if (file == NULL) {
fprintf(stderr, "Error opening file %s\n", argv[i]);
continue;
}

char buffer[1024];
while (fgets(buffer, sizeof(buffer), file) != NULL) {
fprintf(stdout, "%s", buffer);
}

fclose(file);
}

return 0;
}