cs144-lab0
CS144: 计算机网络导论 2025年冬季
check0:
本次实验中大概四项任务:
- 在电脑上安装GNU/Linux系统;
- 手动执行网络任务(如获取网页、发送邮件);
- 用C++编写一个从互联网获取网页的小程序;
- 实现网络的核心抽象之一:内存中的可靠字节流(在写入端和读取端之间)。
1 配置GNU/Linux环境
在这里我选择了腾讯云服务器,随便选了一个免费的。
在云服务器中安装一大堆依赖:
sudo apt update && sudo apt install git cmake gdb build-essential clang \
clang-tidy clang-format gcc-doc pkg-config glibc-doc tcpdump tshark
注意到可以通过ssh连接本地,以便于通过vscode控制服务器,所以花了一部分时间配置ssh。
在本地使用git bash的命令生成isa码。
ssh-keygen -t rsa -C "自己的邮箱"
会在~/.ssh/ 目录下生成 id_rsa(私钥)和 id_rsa.pub(公钥),私钥不用管,把公钥复制下来,在腾讯云服务器手动配置ssh。
mkdir -p ~/.ssh
echo "你的公钥内容" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
这样就可以保证服务器收到公钥后同意连接了。
接下来是vscode的配置,通过配置本地的vscode,以保证vscode能正确的发送ssh请求。
打开vscode后,ctrl+shift+p打开命令行,输入remote ssh,点打开ssh配置文件,会安装一堆插件,直接无脑安装即可

之后打开ssh配置文件,点第一个config文件。

大概率config是空白,按以下格式输入就行。
Host tencent-cloud(这个地方随便输入啥都行)
HostName 114.514.0(自己服务器的公网ip地址)
User ubuntu(在服务器上输入whoami命令,会返回用户名)
Port 22
然后再点ctrl+shift+p就可以连接到主机了

2 使用命令行上网
2.1 获取网页
日常里,在浏览器我们可以直接输入 http://cs144.keithw.org/hello 以查看结果。 而现在,我们需要手动模拟浏览器的行为。
-
在vscode终端中,运行以下命令,以连接服务器。
telnet cs144.keithw.org httptelnet实际上与ssh类似,都是一个实用的远程连接命令,该命令允许用本地计算机去连接cs144网页的服务器,我们在本地的命令可以在服务器上运行。
正常情况下,会得到以下执行结果:
lighthouse@VM-16-14-ubuntu:~$ telnet cs144.keithw.org http Trying 104.196.238.229... Connected to cs144.keithw.org. Escape character is '^]'.这条执行结果告诉我们连接上了cs144这个网站,
Escape character is '^]'.是telnet的提示,表示我们可以按Ctrl + ]进入telnet的命令模式(退出、关闭连接等),我们暂时不用管。依次输入:
GET /lab0/114514 HTTP/1.1 Host: cs144.keithw.org Connection: close按回车发送空行,如果接收到以下响应,就表示请求成功了。
HTTP/1.1 200 OK Date: Mon, 10 Feb 2025 11:22:42 GMT Server: Apache Last-Modified: Thu, 13 Dec 2018 15:45:29 GMT ETag: "e-57ce93446cb64" Accept-Ranges: bytes Content-Length: 14 Connection: close Content-Type: text/plain Hello, CS144! Connection closed by foreign host.我们来一行行观察一下这些命令:
-
GET /hello HTTP/1.1这行表示客户端向服务器发出了一个
GET请求,请求资源/hello。GET是一种 HTTP 请求方法,表示客户端请求获取指定的资源 -
Host: cs144.keithw.orgHost表示目标服务器的主机名或域名。这里是cs144.keithw.org,告诉服务器哪个虚拟主机处理请求
在相应部分中:
HTTP/1.1 200 OK: 表示/hello请求已成功处理。Content-Type: text/plain: 表示返回的数据类型是纯文本(text/plain)。Content-Length: 14: 表示响应体的长度为 14 字节,意味着返回的内容长度是 14 字符。Hello, CS144!: 这是响应体的内容,即服务器返回的数据。客户端请求/hello时,服务器返回了Hello, CS144!这段文本。
也就是说,这些指令等效于直接输入
cs144.keithw.org/hello,而且可以得到hello,cs144!这段文本:
任务是手动获取URL
http://cs144.keithw.org/lab0/sunetid(替换为你的SUNet ID),从响应头X-Your-Code-Is中提取密钥。那我们只要修改
get请求就好了Host: cs144.keithw.org Connection: close HTTP/1.1 200 OK Date: Mon, 10 Feb 2025 11:50:10 GMT Server: Apache X-You-Said-Your-SunetID-Was: 114514 X-Your-Code-Is: 45573 Content-length: 110 Vary: Accept-Encoding Connection: close Content-Type: text/plain Hello! You told us that your SUNet ID was "114514". Please see the HTTP headers (above) for your secret code. Connection closed by foreign host. -
2.2 给自己发送一封邮件
本小节要求使用可靠字节传输流(reliable byte stream)发送邮件给另一台电脑了,但是由于各个邮箱并没有给权限,且我们不是斯坦福大学学生,所以本小节是无法完成的。
-
在原课程中,要求我们通过SSH登录Stanford网络:
telnet 148.163.153.234 smtp -
按顺序输入SMTP命令:
HELO mycomputer.stanford.edu MAIL FROM: <sunetid@stanford.edu> RCPT TO: <sunetid@stanford.edu> DATA From: sunetid@stanford.edu To: sunetid@stanford.edu Subject: Hello from CS144 Lab 0 QUIT
2.3 监听与连接
上面两节或许让你对telnet有了理解:它是一个客户端程序,可以与其他计算机(服务器)程序建立连接,现在尝试成为服务器——等待客户端的连接程序。
-
启动服务端
在虚拟机的终端1运行
netcat -v -l -p 9090这是在终端1上启动一个监听端口为
9090的服务端,使用netcat(简称nc) 工具来监听该端口。-v是指启用详细模式(verbose),可以显示更多的调试信息。-l表示让netcat进入监听模式,等待客户端连接。-p 9090指定监听端口为9090。
执行该命令后,你应该会看到类似于以下的输出:
Listening on [0.0.0.0] (family 0, port 9090)这表明
netcat正在监听端口9090,等待客户端连接。 -
启动客户端:
在虚拟机上再打开一个终端2,如果是ssh连接的话就再打开一个vscode,运行:
telnet localhost 9090这是在终端2上执行的命令,目的是连接到在终端1上运行的服务端。
localhost是指本地计算机的 IP 地址。9090是前面指定的服务端口号。
使用telnet工具可以与监听端口建立连接,并进行简单的文本通信。
如果一切正常,你应该会看到类似的输出:
Trying 127.0.0.1...Connected to localhost. Escape character is '^]'.这表明
telnet成功连接到了服务端。 -
进行双向通信
在任意一个终端(客户端或服务端)键入内容,你会看到对方窗口立即显示你输入的内容。
例如,在
telnet窗口中输入Hello,然后按回车键,你会看到netcat服务端窗口会显示出Hello,反之亦然。 -
终止连接:
在
netcat端,按下Ctrl-C会结束服务端进程,连接将被关闭。此时,telnet客户端也会自动断开连接并退出。Connection closed by foreign host.
由于是该过程是使用本地连接(localhost),所以整个过程不需要依赖外部网络。
3 使用流套接字(stream socket)编写网络程序
在本部分,你将写一个能通过互联网获取页面的简短程序,利用 Linux 内核和大多数其他操作系统提供的功能,以在两个程序之间创建可靠的双向字节流:一个程序在您的计算机上运行,另一个程序在另一台计算机上运行。
这个功能称之为“流套接字”(stream socket),无论是对于您的程序还是对服务器来说,套接字看起来像一个普通的文件描述符。当两个流套接字连接时,写入一个套接字的任何字节最终都会以相同的顺序从另一台计算机上的另一个套接字输出。
事实上,互联网并不会真正的提供可靠字节流连接,相反,互联网真正做的就是尽“最大努力”将短小的数据片段传送到目的地,这短小的数据片称之为数据报,而每个数据报都包含一些报头,它指定了源地址和目标地址等信息——它来自哪台计算机,要发送到哪台计算机——以及要传送到目标计算机的一些有效载荷数据(最多约 1,500 字节)。
尽管互联网是尽力传送每个数据报,但数据报依然会:
- 丢失
- 无序传送
- 传送时内容发生更改
- 重复传送和多次传送
因此,将“尽力而为的数据报”转换为“可靠字节流”实际是两端的操作系统通常负责。
两台计算机必须合作,以确保流中的每个字节传送到另一端的流套接字时都能在正确位置。它们还必须告诉对方它们准备从另一台计算机接收多少数据,并确保发送的数据不超过另一端愿意接受的数据。所有这些都是使用 1981 年制定的约定方案完成的,该方案称为传输控制协议 (TCP)。
在本lab中,我们将编写一个名为“webget”的程序,该程序创建 TCP 流套接字、连接到 Web 服务器并获取页面——请注意,这个过程依赖于操作系统内部的 TCP/IP 协议栈,我们只是调用系统提供的 socket API,而不需要自己管理数据包的可靠传输、重传、流量控制等底层细节。因此,我们只是站在 TCP 之上的应用层操作,比如建立连接、发送 HTTP 请求、接收数据,不过,在未来的lab中,我们会通过自己实现TCP来从不太可靠的数据报中创建可靠的字节流。
3.1 配置代码仓库
-
我们使用名为“Minnow“的代码库,在虚拟机上执行:
git clone git@github.com:CS144/minnow.git cd minnow在GitHub创建私有仓库“minnow”,并添加协作者
cs144-grader(用以评分) -
关联本地与远程仓库:
git remote add github git@github.com:username/minnow.git git push github-
git remote add:添加一个远程仓库(remote repository)。 -
github:这是你为这个远程仓库取的名字,后续git push github会用到它。 -
git@github.com:username/minnow.git:使用SSH连接GitHub,username需要替换成你的 GitHub 用户名。
这样就能让本地的 Git 仓库知道远程 GitHub 仓库的存在,并建立联系。之后,就可以使用
git push和git pull与 GitHub 交互。我们可以使用一行命令来查看连接结果:
git remote -v如果出现了以下结果:
lighthouse@VM-16-14-ubuntu:~/minnow$ git remote -v github git@github.com:seekerzhz/minnow.git (fetch) github git@github.com:seekerzhz/minnow.git (push) origin git@github.com:CS144/minnow.git (fetch) origin git@github.com:CS144/minnow.git (push)就说明连接成功了。
-
3.2 编译代码
cmake -S . -B build
cmake --build build
在执行第一条命令的时候,可能出现如下结果:
lighthouse@VM-16-14-ubuntu:~/minnow$ cmake -S . -B build
CMake Error at CMakeLists.txt:1 (cmake_minimum_required):
CMake 3.24.2 or higher is required. You are running version 3.22.1
-- Configuring incomplete, errors occurred!
cmake版本太低了,我们查看一下cmake的版本:
lighthouse@VM-16-14-ubuntu:~/minnow$ cmake --version
cmake version 3.22.1
CMake suite maintained and supported by Kitware (kitware.com/cmake).
毕竟是kitware提供的,即便我们执行更新指令也没有办法更新,所以我们可以从snap更新cmake:
sudo snap install cmake --classic
只是会出现这种情况:
lighthouse@VM-16-14-ubuntu:~/minnow$ sudo snap install cmake --classic
cmake 3.31.5 from Crascit✓ installed
lighthouse@VM-16-14-ubuntu:~/minnow$ cmake --version
cmake version 3.22.1
CMake suite maintained and supported by Kitware (kitware.com/cmake).
lighthouse@VM-16-14-ubuntu:~/minnow$ which cmake
/usr/bin/cmake
虽然更新了,但cmake用的还是user/bin的cmake,我们只要改一下路径,手动将 /usr/bin/cmake 替换为 Snap 版本:
sudo ln -sf /snap/bin/cmake /usr/bin/cmake
然后再次运行:
cmake --version
此时应该会显示 3.31.5。
cmake -S . -B build
-S .:指定 源代码 目录为当前目录 (.)。-B build:指定 构建目录 为build,所有编译文件会存放在这个build目录中,而不会污染源代码目录。
执行后, build/ 目录被创建,其中包含 CMake 生成的构建文件。
同时,在执行cmake --build build的时候也会出现问题
-- Building in 'Debug' mode.
-- Configuring done (0.2s)
-- Generating done (0.3s)
-- Build files have been written to: /home/lighthouse/minnow/build
[ 5%] Building CXX object util/CMakeFiles/util_debug.dir/debug.cc.o
[ 21%] Built target minnow_debug
[ 26%] Building CXX object tests/CMakeFiles/minnow_testing_debug.dir/common.cc.o
In file included from /home/lighthouse/minnow/util/debug.cc:1:
/home/lighthouse/minnow/util/debug.hh:3:10: fatal error: format: No such file or directory
3 | #include <format>
| ^~~~~~~~
compilation terminated.
make[2]: *** [util/CMakeFiles/util_debug.dir/build.make:93: util/CMakeFiles/util_debug.dir/debug.cc.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:2786: util/CMakeFiles/util_debug.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
In file included from /home/lighthouse/minnow/tests/common.hh:4,
from /home/lighthouse/minnow/tests/common.cc:1:
/home/lighthouse/minnow/util/debug.hh:3:10: fatal error: format: No such file or directory
3 | #include <format>
| ^~~~~~~~
compilation terminated.
make[2]: *** [tests/CMakeFiles/minnow_testing_debug.dir/build.make:79: tests/CMakeFiles/minnow_testing_debug.dir/common.cc.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:2978: tests/CMakeFiles/minnow_testing_debug.dir/all] Error 2
make: *** [Makefile:101: all] Error 2
你妈的,没有format库啊。format是cpp20新增的特性,这时候应该更新gcc了
添加 Ubuntu 官方的 GCC PPA(这是一个提供更新版本的包源):
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt update
安装新版本的 GCC,至少是GCC 13才支持format。
sudo apt install gcc-13 g++-13
切换到安装的 GCC 版本:
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13
验证是否成功切换:
gcc --version
lighthouse@VM-16-14-ubuntu:~/minnow$ gcc --version
gcc (Ubuntu 13.1.0-8ubuntu1~22.04) 13.1.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
ok了。
3.3 现代C++编程规范
CS144是一个以编程为主的课程。实验作业使用现代C++的编程风格,采用2011年及以后的新特性,目标是让代码尽可能安全。这种风格与之前的C++编程方式有所不同。更多相关信息可以参考C++核心指南。
我们的基本目标是:确保每个对象的公共接口尽可能小,内部进行严格的安全检查,避免错误使用,并能在不需要时自动清理自己。我们希望避免需要手动配对的操作(例如:malloc/free 或 new/delete),因为会发生操作没有正确配对(例如:函数提前返回或抛出异常)。所以,我们希望通过构造函数来完成资源的初始化,并在析构函数中自动清理资源。这种做法叫做“资源获取即初始化”(RAII,Resource Acquisition Is Initialization)。
我们需要遵循以下规则:
- 使用官方的C++文档(https://en.cppreference.com)作为参考。(不建议使用
cplusplus.com,因为它可能已经过时。) - 永远不要使用
malloc()或free()。 - 永远不要使用
new或delete。 - 尽量避免使用原始指针(
*),仅在绝对必要时才使用智能指针(unique_ptr或shared_ptr)。在这门课程中不需要这些。 - 避免使用模板、线程、锁和虚函数。在这门课程中也不需要这些。
- 避免使用 C 风格的字符串(如
char *str)和字符串函数(如strlen()、strcpy()),这些容易出错。尽量使用std::string来处理字符串。 - 不要使用 C 风格的强制类型转换(如
(FILE *)x),如果需要转换类型,使用 C++ 的static_cast(不过在这门课中你通常不需要这么做)。 - 优先使用常量引用(
const)来传递函数参数,例如:const Address &address。 - 让每个变量都成为常量(
const),除非必须修改它。 - 让每个方法都成为常量(
const),除非方法需要修改对象。 - 避免使用全局变量,并尽可能缩小每个变量的作用范围。
- 在提交作业之前,请运行
cmake --build build --target tidy来查看代码中可以改进的地方,运行cmake --build build --target format来统一格式。
关于使用 Git:
实验作业是通过 Git(版本控制工具)进行分发的。Git 可以帮助记录你做过的所有更改,便于调试和追踪代码的历史。我们建议你在编写代码时,频繁进行小的提交,并且每次提交时都要写清楚修改了什么,以及为什么做这个修改。理想情况下,每次提交后,代码应该可以正常编译,并且随着时间的推移,测试逐渐通过。频繁的小提交有助于调试(因为每次提交都会编译,而且提交信息清晰地说明了修改的目的),同时也能防止出现作弊的嫌疑,因为你可以清楚地看到自己在每个阶段的进展。这也会帮助你在未来的编程职业中更好地管理代码。
评分人员会根据你的 Git 提交记录,了解你是如何逐步完成实验的。如果你不懂如何使用 Git,可以向 CS144 办公室寻求帮助,或查看一些 Git 教程(例如:https://guides.github.com/introduction/git-handbook)。最后,请确保将代码提交到一个私有的 GitHub 仓库,确保代码不会公开访问。
再次强调:在工作过程中,要频繁进行小的提交,并写清楚每次提交的修改内容和原因。
3.4 阅读 Minnow 支持代码
为了支持这种编程风格,Minnow 的类将操作系统函数(可以通过 C 调用)封装在“现代”C++中。我们为一些你应该在 CS 111 课程中熟悉的概念提供了 C++ 封装,特别是套接字和文件描述符。
请阅读 util/socket.hh 和 util/file descriptor.hh 文件中的公共接口部分(即文件中“public:”之后的内容)。需要注意的是,Socket 是 FileDescriptor 类型的一种,TCPSocket 是 Socket 类型的一种。
3.5 编写 webget
现在是时候实现 webget 程序了,它将通过操作系统的 TCP 支持和流套接字抽象来获取网页——就像你在本实验中之前手动完成的那样。
-
从构建目录中,使用文本编辑器或 IDE 打开
../apps/webget.cc文件。 -
在
get_URL函数中,按照文件中描述的方式实现一个简单的 Web 客户端,使用你之前使用过的 HTTP 请求格式。使用TCPSocket和Address类。 -
提示:
- 在 HTTP 请求中,每一行都必须以 “\r\n” 结束(仅使用“\n”或
endl是不够的)。 - 别忘了在请求中包含
Connection: close这一行。这告诉服务器,在处理完当前请求后,客户端不会再发送任何请求。服务器会发送一个响应后立即关闭与客户端的连接。你将通过检测“EOF”(文件结束)来知道服务器已经完成了响应,因为当服务器的字节流完全传输完毕时,你的套接字会到达 EOF。 - 确保从服务器读取并打印所有输出,直到套接字达到“EOF”(文件结束)——单次调用
read并不足够。 - 我们预计你大约需要写 10 行代码。
- 在 HTTP 请求中,每一行都必须以 “\r\n” 结束(仅使用“\n”或
-
通过运行
cmake --build build .编译程序。如果看到错误消息,你需要在继续之前修复它。 -
通过运行
./apps/webget cs144.keithw.org /hello来测试你的程序。试试看你从浏览器访问http://cs144.keithw.org/hello时的结果和程序输出有什么区别?它与第 2.1 节的结果有何不同?你可以自由尝试——测试任何 HTTP URL! -
当程序似乎正常工作时,运行
cmake --build build --target check_webget来运行自动化测试。在实现get_URL函数之前,你应该看到如下输出:cmake --build build --target check_webget Test project /home/cs144/minnow/build Start 1: compile with bug-checkers 1/2 Test #1: compile with bug-checkers ........ Passed 1.02 sec Start 2: t_webget 2/2 Test #2: t_webget .........................***Failed 0.01 sec Function called: get_URL(cs144.keithw.org, /nph-hasher/xyzzy) Warning: get_URL() has not been implemented yet. ERROR: webget returned output that did not match the test's expectations完成作业后,你应该看到:
cmake --build build --target check_webget Test project /home/cs144/minnow/build Start 1: compile with bug-checkers 1/2 Test #1: compile with bug-checkers ........ Passed 1.09 sec Start 2: t_webget 2/2 Test #2: t_webget ......................... Passed 0.72 sec 100% tests passed, 0 tests failed out of 2 -
评分人员将使用与你的单元测试不同的主机名和路径来运行你的
webget程序,因此请确保它不仅能在单元测试使用的主机名和路径上运行。
4 内存中的可靠字节流
到目前为止,你已经看到如何通过可靠字节流的抽象来进行网络通信,尽管互联网本身只提供“尽力而为”(不可靠)的数据报服务。
在本周实验的最后,你将实现一个对象,它在单台计算机的内存中提供这个抽象(你可能在 CS 110/111 中做过类似的工作)。字节被写入“输入”端,并可以按相同的顺序从“输出”端读取。字节流是有限的:写入方可以结束输入,之后不能再写入字节。当读者读取到流的末尾时,它将遇到“EOF”(文件结束),并且不能再读取字节。你的字节流还将进行流量控制,以限制其在任何给定时刻的内存消耗。该对象初始化时会设置一个“容量”,即它在任何给定时刻愿意存储的最大字节数。字节流将限制写入方在任何给定时刻的写入量,以确保流不会超出其存储容量。当读者读取字节并将其从流中移除时,写入方才可以继续写入。
请注意:字节流是有限的,但在写入方结束输入并完成流之前,它几乎可以是任意长的。你的实现必须能够处理远远超过容量的流。容量限制的是当前时刻内存中存储的字节数(即已写入但尚未读取的字节数),但并不限制流的长度。一个容量仅为一个字节的对象仍然可以承载一个长达数TB的流,只要写入方一次只写一个字节,且读取方在写入方允许写入下一个字节之前读取完每个字节。
以下是写入方的接口:
void push(std::string data); // 向流中推送数据,但仅限于可用容量允许的字节数。
void close(); // 表示流已经结束,之后不能再写入。
bool is_closed() const; // 流是否已经关闭?
uint64_t available_capacity() const; // 当前可以写入多少字节?
uint64_t bytes_pushed() const; // 总共推送到流中的字节数
以下是读取方的接口:
std::string_view peek() const; // 查看缓冲区中的下一个字节
void pop(uint64_t len); // 从缓冲区中移除 `len` 字节
bool is_finished() const; // 流是否已经完成(已关闭且完全被读取)?
bool has_error() const; // 流是否发生了错误?
uint64_t bytes_buffered() const; // 当前缓冲的字节数(已推送但未读取)
uint64_t bytes_popped() const; // 总共从流中移除的字节数
打开 src/byte_stream.hh 和 src/byte_stream.cc 文件,并实现提供此接口的对象。在开发字节流实现时,你可以通过运行 cmake --build build --target check0 来运行自动化测试。
如果所有测试通过,check0 测试将会运行一个速度基准测试。对于这门课程,只要你的实现速度超过 0.1 Gbit/s(即每秒 1 亿比特),对于三种 pop 长度的测试而言就是合格的。(实现速度可能会超过 10 Gbit/s,但这取决于你的计算机速度,并不是必需的。)
对于任何最后时刻出现的问题,请查阅实验的 FAQ 或向同学或教学人员询问,或者在实验课时(或 EdStem)提问。
接下来做什么? 在接下来的四周里,你将实现一个系统,提供相同的接口,但不再是内存中,而是通过不可靠的网络。这就是传输控制协议(TCP)——它的实现可以说是世界上最普遍使用的计算机程序。
5 提交
请注意以下几点
- 在提交时,请只修改
webget.cc和src目录下的源代码(byte_stream.hh和byte_stream.cc)。请不要修改任何测试代码或util中的辅助代码。 - 记得在编写代码时进行小的提交,并写出清晰的提交消息。每次提交后,记得将你的 VM 仓库备份到私有的 GitHub 仓库中,方法是运行
git push github。你的代码需要被提交并推送到 GitHub,才能进行评分。 - 提交作业前,请按以下顺序执行: (a) 确保所有更改都已提交到 Git 仓库。你可以运行
git status来确保没有未提交的更改。记住:在编码时进行小的提交。 (b) 运行cmake --build build --target format(以规范代码风格) (c) 运行cmake --build build --target check0(确保自动化测试通过) (d) 可选:运行cmake --build build --target tidy(建议改进以遵循良好的 C++ 编程实践) - 完成编辑
writeups/check0.md,填写本次作业所花费的时间和任何其他评论。 - 确保你的代码已提交并推送到私有 GitHub 仓库(
git push github)。 - 你需要在 Gradescope 上提交提交 ID,提交截止时间为周日晚上 11:59。
- 如果遇到任何问题,请尽快在周三的实验课时向课程工作人员报告,或在 EdStem 上提问。祝你好运,欢迎来到 CS144。

浙公网安备 33010602011771号