GCC 使用及技巧

本文仅介绍 GCC 的使用以及在 OI 中使用的技巧,不涉及任何工程项目!

介绍

GCC 不用多说,是指的 GNU 编写的 C  C++ 编译器。包含 gcc、g++、gdb 等软件。

安装

大多数 C++ 的 IDE 都集成了 GCC(除了 Visual Studio 使用的 MSVC 和 Xcode 默认使用的是 Clang)(Visual Studio Code 和 Vim 等属于编辑器,不含有 GCC),若要安装 GCC 也很简单。

Windows 上可选择 MinGW-w64,macOS 可在安装 Homebrew 的基础上在终端输入 brew install gcc 安装。

Linux 上可使用 sudo apt-get install build-essentia 命令(对于 Debian 及其衍生发行版),对于其他发行版则使用 sudo pacman -S gcc g++(使用 Arch Linux 举例)。

如果你是 Windows 10 或 Windows 11 用户,强烈推荐使用 WSL2 安装 Ubuntu,在 Ubuntu 中使用 GCC。这样可以避免本地与评测的环境差异。

安装成功后可用 gcc -vg++ -v 检查安装。若安装成功应为下列输出:

1
2
3
4
5
6
7
8
9
10
xenonwzh@XenonWZH-Surface:~$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 9.4.0-1ubuntu1~20.04.1' --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-9-Av3uEd/gcc-9-9.4.0/debian/tmp-nvptx/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)

部分 IDE 与编辑器下 GCC 的设置

Dev-C++

Dev-C++ 可在菜单栏中选择 工具,编译选项。之后会弹出下列窗口。

在窗口中“编译时加入一下命令”打勾,可在里面输入 GCC 参数(不含 -o test)。

Visual Studio Code

Code Runner

在 Visual Studio Code 设置中点击“打开设置(json)”,在该 json 文件最外层大括号末尾添加下列语句:

1
2
3
"code-runner.executorMap": {
"cpp": "cd $dir && g++ $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt"
}

即可在 "cpp" 对应的字符串中修改 g++ 参数。

C / C++ 官方扩展

在 Visual Studio Code 中打开编写程序的文件夹,打开一个 C++ 文件,在菜单栏中点击 终端、配置默认生成任务。之后在弹出的 json 文件中 修改 "args" 参数即可。

clangd

打开文件夹,在文件夹下新建 compile_flags.txt 文件,在该文件中添加 GCC 编译选项即可(同样不含 -o test

编译

下文将以 g++ 举例,gcc 同理。

GCC 可编译单文件,语法如下:

1
g++ test.cpp

此时可生成一个文件 a.out(Windows 下为 a.exe)。

若想让生成的程序变成指定文件名,可使用下列语句:

1
g++ test.cpp -o test

此时会生成名为 test 的文件(Windows 下为 test.exe

若想使用新版本的特性,可使用下列指令(以 ISO C++ 14 为例)(目前的 NOI 系列比赛使用的 GCC 已默认开启 ISO C++ 14):

1
g++ test.cpp -std=c++14

若想开启 O2 优化,可使用下列指令目前的 NOI 系列比赛使用的 GCC 默认开启 O2):

1
g++ test.cpp -O2

若想使用 GDB 调试,则可以使用下列语句:

1
2
g++ test.cpp -g
gdb a.out

以上如 -o-std=c++14-O2-g 以及下列将介绍的参数均可叠加使用。

与 OI 无关的知识点

众所周知宏观的编译操作分为编译、链接两大操作,GCC 也支持编译以及链接操作。

1
2
g++ test.cpp -c  # 编译,生成对象文件 test.o
g++ test.o # 链接,生成可执行文件 a.out

警告

GCC 编译会产生警告乃至报错,例如在 A + B Problem 中的输出语句忘加分号就会产生下列情况:

1
2
3
4
5
6
7
8
xenonwzh@XenonWZH-Laptop:~$ g++ test.cpp -O2 -o test
test.cpp: In function ‘int main()’:
test.cpp:6:36: error: expected ‘;’ before ‘return’
6 | std::cout << a + b << std::endl
| ^
| ;
7 | return 0;
| ~~~~~~

该情况为报错,导致程序无法通过编译。

又例如在程序中定义了一个变量但始终未使用,编译将出现下列情况:

1
2
3
4
5
xenonwzh@XenonWZH-Laptop:~$ g++ test.cpp -O2 -Wall -o test
test.cpp: In function ‘int main()’:
test.cpp:4:15: warning: unused variable ‘c’ [-Wunused-variable]
4 | int a, b, c;
|

该情况为警告,程序可以通过编译,但可能在运行中得不到想要的答案,若你对自己的程序很有把握可以忽略警告。

产生更多的警告

由于 GCC 能产生的警告少之又少,同时如 === 不分等常见错误在 GCC 的默认设置下不会警告,所以我们一般会在编译中添加参数以生成更多的警告,用法如下:

1
g++ test.cpp -Wall

利用该命令就可以产生如上一个例子的警告。

忽略特定的警告

由于开启了 -Wall 容易误伤,所以我们可以利用参数忽略特定的警告。

例如我们使用了 scanf 函数,但是 GCC 警告我们没有使用该函数的返回值:

1
2
3
4
test.cpp: In function ‘int main()’:
test.cpp:18:14: warning: ignoring return value of ‘int scanf(const char*, ...)’, declared with attribute warn_unused_result [-Wunused-result]
18 | scanf("%d%d%d", &x, &y, &z);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~

我们可以在其中提取警告的类型。我们看到了 [-Wunused-result],说明该类型为 unused-result

我们可以使用 -Wno 参数来忽略警告,使用时应在 -Wno 后紧跟警告类型,例子如下:

1
g++ test.cpp -Wall -Wno-unused-result

接下来就没有烦人的未使用 scanf 返回值的警告了。

宏定义

我们可以利用 GCC 编译来代替 C++ 中简单的宏定义。

参数为 -D,使用时应紧跟定义宏。例子如下:

1
g++ test.cpp -DDEBUG

其中 -DDEBUG 相当于在 test.cpp 中添加下列语句:

1
#define DEBUG

该参数可在调试或其他场合中使用,非常方便。

例如把调试的语句在定义 DEBUG 宏时才执行,文件输入输出在没定义 DEBUG 时执行,在考场上可有效避免非知识原因的爆 0。

同时注意在绝大多数 OJ 编译代码时参数都含有 -DONLINE_JUDGE,可以利用该特性提升刷题体验。