Skip to content

0.2 Windows 上的 C++ 工具链

你在图形化 IDE 中点击「运行」时,背后至少发生了这些事情:编辑器保存源文件,编译器把源文件翻译成目标文件,链接器把目标文件和库组合起来,操作系统再启动生成的可执行文件。

这些环节都被 IDE 包办,所以刚开始写代码会很舒服。但当遇到找不到头文件、不支持新特性这类错误,就很难判断问题究竟出在哪里。而且,我们将在后续的第三章中详细介绍构建系统、CMake、测试和 CI,因此了解 IDE 实际如何工作是十分有必要的。

考虑到其实大多数初学者仍然使用的是 Windows 系统,使用 WSL 或迁移到 Linux 将会是不小的负担(尤其考虑在中国大陆地区还需要配置镜像或网络代理,会进一步增加复杂度因此本节将介绍在 Windows 上安装并认识一个可以从命令行使用的 GCC C++ 环境。

理解工具链角色

在动手之前我们先来了解我们使用的工具链各自承担什么角色。

GCC,GNU Compiler Collection,GNU 编译器集合,其中的 g++ 是 C++ 编译驱动程序。我们接下来主要使用它作为编译器。libstdc++ 是 GCC 生态中的 C++ 标准库实现,提供 <vector><string><span> 等头文件和库。

但要想生成 Windows 原生程序,则需要有一套工具链接 GCC 与 Windows API、C 运行库。在早期 MinGW(Minimalist GNU for Windows)承担了这个角色。但由于其功能受限、仅支持 32 位,后来又出现了 MinGW-w64 项目,是现在的主力。我们使用 #include <windows.h>HWNDMessageBox 这些 Windows 接口时,依赖的就是它。

Clang 是另一款编译器,以 clang++ 作为驱动程序。我们这里暂时不用,后续详细介绍。

在 Windows 这边,系统也为 C / C++ 准备了运行时 —— C Runtime,缩写 CRT。在早期使用 MSVCRT(Microsoft Visual C Runtime更现代的运行时是 UCRT(Universal C Runtime虽然运行时由 Windows 提供,但在编译时需要选择到时候需要调用哪套运行时。我们现在一般使用 UCRT。

这一套配置十分复杂。因此便有一些简化的尝试。在 Dev-C++ 中内置的 TDM-GCC,便是一个第三方打包好的 GCC 工具链发行版。它包含了 GCC、MinGW-w64、binutils 和各种配置,整合在一个安装包里,方便用户安装使用。但 TDM - GCC 的最新正式 release 目前还是 GCC 10.3.0,发布时间已经比较久了,不太建议现在再使用。

现在一般推荐使用 MSYS2 进行配置。它是一个开发环境或者说软件生态,将 Bash、Autotools、Make、Git、GCC、GDB 等全部整合进来,为 Windows 提供一套「类似 Linux」的开发体验。它使用从 Arch Linux 移植过来的 pacman 作为包管理器,管理这些软件。

MSYS2 提供了若干「环境」以方便管理。所谓的「环境」规定了架构、编译器、运行时等,类似一张餐桌加一份菜谱,让 pacman 安装即可开始享用。需要注意:MSYS2 提供的几个环境的名字很有迷惑性:我们会看到 MSYS、MINGW64、UCRT64、CLANG64,它们是环境的名字,不是我们前面所说的工具!它们的大致配置如下:

环境编译器接口运行时架构当前定位
UCRT64GCCMinGW-w64UCRTx64推荐
MINGW64GCCMinGW-w64MSVCRTx64旧环境
CLANG64ClangMinGW-w64UCRTx64可用

MSYS2 已在 2026 年开始弃用 MINGW64 环境,这里仍列出它,是因为旧教程和已有安装中经常能看到这个名字。新安装按官方建议选择 UCRT64 即可。

而 MSYS 环境比较特殊,它运行一个自己的 MSYS2 运行时,用来跑 Unix 世界的工具链,其中虽然也有 GCC 但它是用来编译 MSYS 程序的,其中没有 MinGW-w64,也不能用来编译原生 Windows 程序。

安装环境

我们刚才提到,接下来使用 GCC + MinGW-w64 + UCRT,对应 MSYS2 里的 UCRT64 环境。因此我们接下来要:安装 MSYS2、进入其中的 UCRT64 环境、使用 pacman 安装 GCC 等工具链。

MSYS2 官方网站 下载安装器。安装位置建议采用短的 ASCII 路径,例如默认的 C:\msys64。一路下一步之后点击完成。此时开始菜单里会出现 MSYS2 各个环境的快捷方式。打开 MSYS2 UCRT64(注意不是 MSYS2 MSYS

NOTE

如果你想在自己常用的终端软件中打开,启动它的命令行是:

powershell
C:\msys64\msys2_shell.cmd -defterm -here -no-start -ucrt64 -shell bash

启动路径为:

C:\msys64\home\%USERNAME%

如果你自定义了安装路径,需要把 C:\msys64\ 替换为你的安装路径。

打开后,你应该会看到形如:

linho@LinhosPC UCRT64 ~
$

如果你对这样的界面不太熟悉,这里介绍一下:这是 bash 终端,开头的绿色是 用户名@计算机名,紫色的 UCRT64 是当前的环境名称,~ 是当前路径,当前位于 home 目录。$ 后可以输入命令,回车执行。

执行命令更新 MSYS2 本身:

sh
pacman -Suy

如果它提示关闭当前终端,确认后关闭窗口,再重新打开 MSYS2 UCRT64,继续执行一次 pacman -Suy。MSYS2 是滚动更新的发行版,官方建议初次打开时进行完整更新。

更新完成后,在 MSYS2 UCRT64 终端中执行:

bash
pacman -S --needed mingw-w64-ucrt-x86_64-toolchain

这个工具链组除了 GCC,还会安装 binutils、GDB、Make、Windows 头文件和运行库等组件。

此时,执行

sh
g++ --version

应该会看到形如这样的输出:

g++.exe (Rev3, Built by MSYS2 project) 16.2.0
Copyright (C) 2026 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.

这表明 GCC 已经成功安装。

第一次从命令行编译

假设有一个 C++ 源文件 D:\cpp_playground\hello_world.cpp 需要编译。仍然在 MSYS2 UCRT64 终端中,进入目录(盘符 D:\ 使用 /d/ 表示

sh
cd /d/cpp_playground

然后执行:

sh
g++ -std=c++20 -Wall -Wextra -Wpedantic hello_world.cpp -o hello_world.exe

参数的含义是:

参数作用
-std=c++20使用 C++20 语言模式
-Wall开启一组常见警告
-Wextra开启更多额外警告
-Wpedantic对偏离标准的写法给出提示
-o hello_world.exe指定输出文件名

接下来执行

sh
./hello_world.exe

即可启动你的程序。

这就是 IDE 的「编译并运行」按钮最终会调用的某种命令。IDE 可以继续使用,但现在你知道如何绕过它独立验证工具链了。

在其他地方调用

开发工具链本质上还是一堆文件。要从其他地方启动它,则还需要一些配置。在刚才的终端中执行:

sh
where.exe g++

会得到形如 C:\msys64\ucrt64\bin\g++.exe 的路径,这便是 g++ 所在的位置。

如果你只想在 PowerShell 中直接调用 g++,可以考虑将该目录添加到 PATH。不过,MSYS2 环境还会设置 MSYSTEMMINGW_PREFIX 等变量,部分工具不能仅靠添加一个目录完整复现环境。更稳妥的做法是在终端或 IDE 中配置 MSYS2 UCRT64 shell;只调用原生的 g++.exe 时再使用 PATH。

你也可以设置现有的 IDE,使其指向刚才安装的 GCC。

以常用的教学工具 Dev-C++(或者它的修改版,例如小熊猫 C++)为例:

  • 进入 选项 → 编译器 → 配置集,在「编译器配置方案」下拉框的右侧有一个文件夹按钮,点击选择上面的目录,即可自动识别到 GCC 并使用;
  • 在对话框下方的 编译 / 链接选项 → 代码生成 中,可以选择 C++ 语言标准为「ISO C++20
  • 在对话框下方的 编译 / 链接选项 → 代码警告 中,勾选「启用常见问题警告 (-Wall)」和「启用更多问题警告 (-Wextra)

此时编译文件,底部的「工具输出」栏显示:

txt
编译单个文件...
------------------
- 文件名: D:/cpp_playground/hello_world.cpp
- 编译器配置: MinGW-w64 GCC 16.1.0 64-bit Debug

正在处理C++源程序文件:
------------------
C++编译器: C:/msys64/ucrt64/bin/g++.exe
命令: g++.exe D:\cpp_playground\hello_world.cpp -o D:\cpp_playground\hello_world.exe -g3 -fno-ms-extensions -std=c++2a -pipe -Wall -Wextra -Werror=return-type -Werror=vla -D_DEBUG -Wl,--stack,12582912

编译结果:
------------------
- 错误数: 0
- 警告数: 0
- 输出文件名: D:\cpp_playground\hello_world.exe
- 输出文件大小: 400.24 KiB
- 编译时间: 0.47 秒

可以看到编译时调用的便是刚才配置的 GCC 了,也可以看到其实际调用的命令行。

这里的 C++ 标准选项写的是 -std=c++2a。这是 C++20 在标准正式定稿前使用的临时名称,后来 GCC 将正式名称改为 -std=c++20,同时保留了 c++2a 作为兼容别名。