命令行基础
| Resources | |||||
|---|---|---|---|---|---|
| CPH | 简单程序的编译过程 | ||||
Linux
| Resources | |||||
|---|---|---|---|---|---|
| William Shotts | |||||
| Ubuntu.com | 适用于 Linux、Mac 和所有 Unix 终端 | ||||
Mac
基本上与 Linux 相同……
打开终端应用并熟悉一些基本命令。如果还没有升级,请改用 zsh。
| Resources | |||||
|---|---|---|---|---|---|
| Jim Hoskins | |||||
| Rahul Saigal | 键盘快捷键与终端命令 | ||||
| Armin Briegel | |||||
Windows
| Resources | |||||
|---|---|---|---|---|---|
| Princeton | Java | ||||
| ComputerHope | |||||
安装 g++
USACO(以及大多数比赛)使用 GCC 的 g++ 编译并运行代码。要使用 #include <bits/stdc++.h> 头文件,必须安装 g++;详情请参阅在本地运行代码。
在 Linux 上
大多数 Linux 发行版通常预装 GCC。可以用以下命令检查是否已安装:
whereis g++
如果没有预装,通常可以使用发行版的软件包管理器进行安装。
在 Mac 上
安装 XCode 命令行工具。
xcode-select --install
如果以前安装过,可能需要进行更新:
softwareupdate --list # list updates softwareupdate -i -a # installs all updates
完成这一步后,
clang应已安装(尝试在终端中运行clang --version)。安装 Homebrew。
使用 Homebrew 安装
gcc。brew install gcc
根据这里的说明,如果
brew长时间无法完成,那么:brew install gcc --force-bottle
通常就足以解决问题。
此时应能使用
g++-#编译,其中 # 是版本号(例如 10)。运行下面的命令:g++-10 --version
应显示类似以下内容:
g++-10 (Homebrew GCC 10.2.0_2) 10.2.0 Copyright (C) 2020 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.
如果希望只输入
g++就能编译,可以创建 shell 别名。将下面这行放入 shell 的 rc 文件(使用bash时为~/.bashrc,使用zsh时为~/.zshrc)。alias g++=g++-10
完成后,
g++ --version应与g++-10 --version输出相同内容。注意:不要通过符号链接或硬链接覆盖系统的
g++,因为这样几乎肯定会引发问题。如果不知道这些术语的含义,也不必担心。
在 Windows 上
较简单:Mingw-w64(适用于 Windows 的精简 GNU)
| Resources | |||||
|---|---|---|---|---|---|
| Microsoft | 在 VS Code 中配置 MinGW | ||||
| Jetbrains | 在 CLion 中配置 MinGW | ||||
较困难:适用于 Linux 的 Windows 子系统(WSL)
如果已经习惯 Linux 命令行,这可能是最适合你的选择。
适用于 Linux 的 Windows 子系统通常简称 WSL,它在 Windows 系统内运行 Linux 内核(或模拟层,取决于使用的版本)。这样无需将 Linux 作为主操作系统,也能使用 Linux 二进制程序。
许多人(例如 Anthony)使用 WSL,但正确配置它可能有些困难。
| Resources | |||||
|---|---|---|---|---|---|
| Microsoft | 对新手较为困难 | ||||
如果想使用 (Neo)Vim 编程,可以安装 WSL 并通过 WSL bash 编写代码。
配置 WSL 后,可以运行以下命令安装必要工具。
对于 Ubuntu 等基于 Debian 的发行版:
sudo apt-get install build-essential
对于 Arch Linux 等基于 Arch 的发行版:
sudo pacman -Sy base-devel
网上可以找到许多美化 WSL、改善使用体验的教程。第一步是使用合适的终端,而不是 Windows 提供的默认终端。Microsoft Store 中的 Windows Terminal 是一个易用的选择。
| Resources | |||||
|---|---|---|---|---|---|
配置终端 | |||||
打造美观的命令行界面 | |||||
在命令行中使用 C++
编译与运行基础
考虑下面这个简单程序,并将它保存为 name.cpp。
#include <iostream>using namespace std;int main() {int x;cin >> x;cout << "FOUND " << x << "\n";}
编译并运行 C++ 程序并不困难。首先,在 Windows 上打开 PowerShell,在 Mac 上打开“终端”,或在 Linux 中打开所用发行版的终端。可以使用以下命令,将 name.cpp 编译成名为 name 的可执行文件:
g++ name.cpp -o name
然后执行程序:
./name
输入一个整数并按回车后,程序应当产生输出。也可以将这两个命令写在同一行:
g++ name.cpp -o name && ./name
注意,&& 能确保只有 g++ name.cpp -o name 成功完成后,./name 才会运行。
重定向输入与输出
如果想从 inp.txt 读取标准输入,请使用:
./name < inp.txt
如果想将标准输出写入 out.txt,请使用:
./name > out.txt
两者也可以同时使用,如下所示:
./name < inp.txt > out.txt
关于如何在程序内部进行文件输入输出,请参阅输入与输出。
编译器选项(也称参数)
使用编译器参数可以改变 GCC 编译代码的方式。通常,我们会用类似下面的命令代替 g++ name.cpp -o name:
g++ -std=c++17 -O2 name.cpp -o name -Wall
-std=c++17允许使用 2017 年加入 C++ 的功能。USACO 已从 C++11 升级到 C++17。-Wall会检查程序中的常见错误。更多信息请参阅调试。
编译时应始终使用这些参数。
添加快捷命令(Mac)
面向 Linux 与 Windows 用户
Linux 上的操作过程相似。如果使用 Windows,可以通过 IDE 获得这些快捷功能,也可以安装上文提到的 WSL。
反复输入上述编译器参数十分繁琐。应当定义快捷命令,这样就不必每次重新输入。
| Resources | |||||
|---|---|---|---|---|---|
| Jonathan Suh | |||||
| SO | |||||
首先,如果 .zshrc 尚不存在,请创建它。
touch ~/.zshrc
使用文本编辑器打开 .zshrc。
open ~/.zshrc
也可以使用其他文本编辑器(例如通过 subl 打开 Sublime Text)。
subl ~/.zshrc
可以在这里添加别名和函数,例如用下面的内容在 Mac 上编译并运行 C++:
co() { g++ -std=c++17 -O2 -o "${1%.*}" $1 -Wall; }
run() { co $1 && ./${1%.*} & fg; }
现在可以在命令行中使用 co name.cpp && ./name 或 run name.cpp,轻松编译并运行 name.cpp。注意,函数中所有 $1 都会替换为 name.cpp,而 ${1%.*} 会移除 $1 的文件扩展名,得到 name。
& fg 有什么作用?
假设 prog.cpp 是以下文件:
#include <iostream>#include <vector>using namespace std;int main() {vector<int> v;cout << v[-1];}
根据上面的资料,在 Mac 上要让 zsh 显示崩溃信息(例如段错误),必须使用 & fg。例如,考虑使用 run prog.cpp 运行上面的 prog.cpp。
如果从上面的运行命令中移除 & fg,终端不会显示任何信息;保留它则会产生以下输出(忽略前两行):
[2] 30594
[2] - running ./${1%.*}
zsh: segmentation fault ./${1%.*}
测量时间与内存用量(Mac)
| Resources | |||||
|---|---|---|---|---|---|
| CF | 适用于 Linux | ||||
| SO | 使用 gtime | ||||
例如,假设 prog.cpp 包含以下内容:
#include <bits/stdc++.h>using namespace std;const int BIG = 1e7;int a[BIG];int main() {int sum = 0;for (int i = 0; i < BIG; ++i) sum += a[i];cout << sum;}
那么 co prog.cpp && gtime -v ./prog 会得到以下结果:
Command being timed: "./prog" User time (seconds): 0.01 System time (seconds): 0.01 Percent of CPU this job got: 11% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.22 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 40216 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 91 Minor (reclaiming a frame) page faults: 10088 Voluntary context switches: 3 Involuntary context switches: 38 Swaps: 0 File system inputs: 0 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0
注意, 个整数需要 KB 内存,与上面输出的 十分接近,符合预期。
调整栈大小(Mac)
本节内容可能已经过时。
假设 A.cpp 是以下程序:
#include <iostream>using namespace std;int res(int x) {if (x == 200000) return x;return res(x + 1);}int main() { cout << res(0) << "\n"; }
使用 g++ A.cpp -o A && ./A 编译并运行时,会输出 200000。然而,将 200000 改为 300000 后就会发生段错误。类似地:
#include <iostream>using namespace std;int main() {int arr[2000000];cout << arr[0] << "\n";}
可以正常运行,但将 2000000 改为 3000000 也会发生段错误。这是因为 Mac 的栈大小默认似乎限制为 8 MB。
| Resources | |||||
|---|---|---|---|---|---|
| Wikipedia | 问题说明 | ||||
请注意,除通常的 256 MB 内存限制外,USACO 没有单独的栈大小限制。因此,在本地因栈溢出而崩溃的代码,仍可能在 USACO 服务器上通过。要让代码在本地运行,请使用以下方法之一。
对于 Facebook Hacker Cup 这类需要提交本地程序运行结果的比赛,这一点尤其重要。
方法一
| Resources | |||||
|---|---|---|---|---|---|
| SO | |||||
ulimit -s 65532 会将栈大小增加到约 64 MB。遗憾的是,更大的数值无法通过这种方式生效。
方法二
| Resources | |||||
|---|---|---|---|---|---|
| CF | 人们每年都会抱怨 FHC | ||||
为绕过这一限制,可以传递一个链接器选项。根据 ld 的手册(在终端中输入 man ld),-stack_size size 选项具有以下作用:
指定程序主线程的最大栈大小。不使用该选项时,程序的栈大小为 8 MB。参数 size 是一个十六进制数,可以带有前导 0x。大小应为体系结构页面大小(4 KB 或 16 KB)的倍数。
因此,在编译命令中加入 -Wl,-stack_size,0x10000000,会将最大栈大小设为 字节 MB,通常已经足够。不过,将上面第一个程序中的 200000 替换为 1e7 后运行,仍会报错。这时可以进一步增大最大栈空间(例如将 0x10000000 改为 0xF0000000)。
在 Windows 上,将 -Wl,--stack,268435456 加入编译参数即可达到相同效果。268435456 表示 268435456 字节,也就是 256 MB。如果使用 Windows PowerShell,请务必用引号将它括起来(即 "-Wl,--stack,268435456"),因为逗号会被视为特殊字符。