NOI Linux 中的 G++ 编译
参加 NOI 系列比赛中(如 CSP),那么熟悉 NOI Linux 下的 G++ 编译器是必不可少的。虽然平时在 Windows 上用 Dev-C++ 或者 VS Code(放心考试电脑基本不预装而且不会有 C/C++ 插件)写代码。
但是到了比赛环境下,了解如何使用 G++ 编译器以及常用的编译选项就显得尤为重要。(就算在 Windows 上写最好也可以到 Linux 上测试一下能不能运行。)
通常情况下(包括评测的时候)用的都是 G++ 编译器来编译 C++ 代码。
(一般情况下,在考试用的 Windows 的一个盘会有一个和 Linux 互通的 public 目录,可以直接把 cpp 复制进去。)
G++ 是什么?
G++ 是 GCC(GNU Compiler Collection)的 C++ 编译器。简单来说,它就是把你写的 C++ 代码翻译成计算机能执行的程序。在 NOI Linux(基于 Ubuntu)中,G++ 是默认安装好的,你可以直接在终端里使用。
(终端是 Terminal 应该不用教了吧。)
打开终端输入:
g++ --version
可以看到类似 g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 这样的输出。
最简单的编译
假设你写了一个 hello.cpp 文件,内容大概是这样的:
#include <iostream>
using namespace std;
int main() {
cout << "Hello, NOI!" << endl;
return 0;
}
要把它编译成可执行文件,最简单的命令就是:
g++ hello.cpp
执行完这条命令后,你会发现当前目录下多了一个叫 a.out 的文件(这是默认的输出文件名)。运行它:
./a.out
就能看到 Hello, NOI! 的输出了。
不过这个 a.out 名字有点奇怪对吧?我们可以用 -o 参数给编译出来的程序指定一个名字:
g++ hello.cpp -o hello
这样生成的可执行文件就叫 hello 了,运行的时候也更方便:
./hello
比赛中常用的编译参数
在正式比赛中,评测系统通常会使用特定的编译参数来编译你的代码。了解这些参数很重要,因为它们会影响程序的性能和行为。通常是类似这样的:
g++ source.cpp -o program -O2 -std=c++14 -static
咱们一个一个来看这些参数都是干啥的。
-O2:开启优化
G++ 提供了好几个优化级别:
-O0:不进行优化(这也是默认的)-O1:进行基本的优化-O2:进行更多的优化,这是比赛中最常用的-O3:最激进的优化-Os:优化代码体积
比赛中一般使用 -O2,因为它能在保证正确性的前提下显著提升程序运行速度。你的程序可能会快个几倍,这在时限卡得紧的题目中就很关键了。
举个例子,如果你写了一个暴力算法,没开优化可能会超时,但开了 -O2 后说不定就能过了(当然,更好的做法还是优化算法本身)。
g++ mycode.cpp -o mycode -O2
-std=c++14:指定 C++ 标准
C++ 一直在发展,有 C++98、C++11、C++14、C++17、C++20 等多个版本。不同版本支持的特性不太一样。
在 NOI 系列比赛中,目前(2025 年)大多数比赛使用的是 C++14 标准。这意味着你可以使用 C++11 和 C++14 的新特性,比如:
auto关键字- 范围
for循环(for (auto x : vec)) lambda表达式- 更好用的 STL 容器
nullptr代替NULL
但要注意,C++17 和 C++20 的一些新特性可能就用不了了。
g++ mycode.cpp -o mycode -std=c++14
如果你不指定标准,G++ 会使用默认的标准。不过为了保险起见,建议显式指定 -std=c++14。
-static:静态链接
-static 参数会让编译器进行静态链接,把程序依赖的库都打包到可执行文件里。这样做的好处是程序不怎么依赖动态库,并且避免了动态链接可能带来的一些问题。
不过静态链接也有缺点,就是生成的可执行文件会大很多。这在比赛中一般不是问题,因为对可执行文件大小通常没有限制。
g++ mycode.cpp -o mycode -static
(其实 static 差不多,毕竟 NOI Linux 预装的 G++ 通常编译出来是可以在 NOI Linux 上运行的。)
把它们组合起来
在实际比赛中,你通常会看到这样的编译命令:
g++ mycode.cpp -o mycode -O2 -std=c++14 -static
这条命令做了这些事:
- 编译
mycode.cpp - 输出文件名为
mycode - 开启 O2 级别优化
- 使用 C++14 标准
- 进行静态链接
警告信息
编译器的警告信息经常能帮你发现代码中的潜在问题。虽然有警告不代表程序一定有错(它还是能编译通过的),但很多时候警告指向的地方确实有 bug。
-Wall:显示常规警告
-Wall 会打开一大堆常见的警告选项(虽然名字是 "all",但其实并不是所有警告)。比如:
- 变量定义了但没使用
- 函数应该返回值但没有 return 语句
- 可能的未初始化变量
- 格式化字符串和参数类型不匹配
g++ mycode.cpp -o mycode -Wall
举个例子,如果你的代码是这样的:
#include <iostream>
using namespace std;
int main() {
int x;
cout << x << endl; // x 没有初始化!
return 0;
}
加了 -Wall 后编译会给出警告:
warning: 'x' is used uninitialized in this function
这就提醒你 x 在使用前应该先赋值。
-Wextra:额外的警告
-Wextra 会启用更多的警告,包括一些 -Wall 没有包含的。比如:
- 有符号数和无符号数比较
- 参数未使用(在函数定义中)
- 某些可能的逻辑错误
g++ mycode.cpp -o mycode -Wall -Wextra
注意 -Wextra 通常和 -Wall 一起使用,它们是互补的。
看个例子:
int main() {
int a = 10;
int b; // 没有使用!
cout << a << endl;
}
-Wextra 会提醒你参数 b 定义了但没用到,可能是你忘了什么?
平时练习开着
g++ mycode.cpp -o mycode -O2 -std=c++14 -Wall -Wextra
虽然比赛时评测系统编译你的代码不会开这些警告,但养成习惯能让你的代码质量提高不少。很多低级错误都能在编译阶段就被发现。
当然,有时候你可能会遇到一些「假警告」——代码逻辑其实没问题,但编译器还是报警告(比如 scanf 返回值被忽略)。这种情况下你可以根据具体情况判断。但大多数时候,警告指出的问题是真实存在的。
其他实用的参数
除了上面提到的,还有一些参数在某些情况下也很有用。
-g:生成调试信息
如果你需要用 GDB 调试程序,就需要加上 -g 参数:
g++ mycode.cpp -o mycode -g
这会在生成的可执行文件中包含调试信息,让你能够用 GDB 看到源代码、设置断点、查看变量值等。
-lm:链接数学库
在某些老版本的编译器上,如果你用了 <cmath> 里的函数,可能需要显式链接数学库:
g++ mycode.cpp -o mycode -lm
不过在现代的 G++ 版本中(包括 NOI Linux 用的版本),这通常不是必需的。但了解一下也无妨。
-DDEBUG:定义宏
有时候可能想在代码里写一些调试输出,但提交时又不想让它们运行。可以这样:
#ifdef DEBUG
cerr << "Debug info: x = " << x << endl;
#endif
编译时加上 -DDEBUG 就会定义一个编译时宏,就会启用这些调试代码:
g++ mycode.cpp -o mycode -DDEBUG
不加就不会编译这部分代码。这在调试时很方便。
完整的比赛编译命令
总结一下,如果你要完全模拟 NOI 系列比赛的编译环境,使用这条命令:
g++ source.cpp -o program -O2 -std=c++14 -static
(如果你还想要警告信息帮助你检查代码):
g++ source.cpp -o program -O2 -std=c++14 -static -Wall -Wextra
一些实用技巧
保存编译命令
每次都敲这么长的命令很麻烦对吧?你可以把它保存成一个脚本文件。创建一个 compile.sh 文件:
#!/bin/bash
g++ $1.cpp -o $1 -O2 -std=c++14 -static -Wall -Wextra
然后给它添加执行权限:
chmod +x compile.sh
以后编译的时候只需要:
./compile.sh mycode
就会自动编译 mycode.cpp 生成 mycode 可执行文件了。
编译并运行
如果你想编译完立即运行,可以用 && 连接命令:
g++ mycode.cpp -o mycode -O2 -std=c++14 && ./mycode
只有编译成功了才会运行程序。
批量编译
有时候你可能需要编译多个文件,可以用循环:
for i in *.cpp; do
g++ $i -o ${i%.cpp} -O2 -std=c++14 -static
done
这会把当前目录下所有的 .cpp 文件都编译一遍。
常见问题和注意事项
编译错误 vs 运行错误
编译错误是指代码语法有问题,G++ 无法将其翻译成可执行文件。这时候需要根据错误信息修改代码。
运行错误是指程序能编译通过,但运行时出了问题(比如数组越界、除零等)。这就需要调试了。
不要过度依赖优化
虽然 -O2 很强大,但不要用它来掩盖算法的低效。如果你的算法复杂度就是不够,再怎么优化编译选项也无济于事。该优化算法还是得优化算法。
写在最后
建议在日常练习中就使用和比赛一致的编译选项和编译器,这样能更好地模拟考场环境,避免「本地能过,考场 WA」的尴尬情况。