简介:面向 Windows 平台 C/C++ 开发者的 MinGW-w64 工具链发行版,版本为 13.2.0,采用 POSIX 线程模型与 SEH 异常处理并链接 msvcrt 运行时,适合需要在 Windows 下使用 GNU/GCC 生态、又希望生成原生可执行文件的场景。压缩包共 2000 个文件,体积约 82MB,包含 903 个 .h 与 243 个 .hpp 头文件,覆盖标准库、SQLite、Win32 及编译器内建函数等声明;823 个 .py 脚本多用于构建配置与辅助工具;另有少量 C 源码、Shell 脚本、Markdown 文档与前端文件,便于理解构建流程与查阅说明。资源已有 472 人学习下载,解压后可直接配置 GCC/G++ 环境,并获得与 Linux 相近的编译体验。借助 SEH 异常处理和 msvcrt 兼容性,开发者可方便地链接依赖 C++ 运行库的第三方库,产出可独立分发的 Windows 程序。
1. 这个长文件名到底在说什么:x86_64-13.2.0-release-posix-seh-msvcrt 的组合逻辑
老手拿到x86_64-13.2.0-release-posix-seh-msvcrt-rt_v11-rev1.7z这种压缩包,第一反应不是急着解压,而是把文件名念一遍:架构、编译器版本、线程模型、异常处理模型、C 运行时,一次全列清楚。这是一份 MinGW-w64 的便携发行包,把 GCC 工具链编译成能在 Windows 上直接运行的形态,gcc、g++、make、ld 全都在里面。它解决什么问题?你不想为了编译一个 C++ 程序去安装庞大的商业 IDE,或者你的项目需要在 Windows 上做原生编译并把产物部署到旧机器上,那就需要一套不依赖额外运行时、解压即用的工具链。适合谁用?在 Windows 上做 C/C++ 本地开发、维护构建脚本、给 CI 准备一套可复制编译环境的工程师。
2. 五个命名段拆解与选型:为什么 posix、seh、msvcrt 是最稳的组合
这个 7z 里没有源代码,它是已经编译好的二进制工具链。命名里的每一段都决定你后续能不能编 C++11 线程、产物能不能跑在旧系统上。理解这些段落,比记安装步骤重要,因为不同发行方命名规则略有差异,但字段含义可以通用。
2.1 线程模型:posix 与 win32 的分水岭影响 C++ 标准库
GCC 在 Windows 上的线程模型有两个方向:win32 和 posix。win32 线程模型直接用 Windows API 封装线程,编译出的程序更纯粹,不依赖额外 DLL,但代价是 C++ 标准库里的std::thread、std::mutex、std::async这一整套并发设施直接用不了。原因在于 libstdc++ 的线程封装是建立在 pthread 接口上的,win32 模型没有提供 pthread 映射层,编译器在配置阶段就禁用了这部分功能,你写std::thread t(...)时会得到类似#error "GCC does not support C++11 thread facilities"的报错。
posix 模型则通过 winpthreads 库把 pthread 接口映射到 Windows 线程上,标准库的并发组件全部可用。这个包的线程字段是 posix,意味着你可以在 Windows 上写标准的跨平台多线程 C++ 代码,不需要为 Windows 单独维护一套线程封装。链接时编译器会自动带上 libwinpthread,代价是发布目录里要多放一个 DLL,或者按后面的方式静态链入。
验证当前工具链线程模型有两种方式,一是直接看gcc -v的 configure 参数,二是查编译器的预定义宏:
gcc -v 2>&1 | grep -E "enable-threads|Thread model" echo | gcc -dM -E - | grep -i pthreadThread model: posix一行就是结论;如果检测不到宏也不用紧张,因为 pthread 相关宏不是每次预处理都会暴露。真正能让你直观感受差异的是写法:同一份std::thread代码,在 win32 模型上编译不过,在 posix 模型上编译通过。
2.2 异常模型:x64 平台用 seh 而不是 dwarf 或 sjlj 的取舍
异常模型解决的是 C++ 的throw/catch如何展开调用栈的问题,GCC 在 Windows 上有 seh、dwaf 和 sjlj 三种路子。seh(Structured Exception Handling)是 Windows x64 的官方异常机制,微软的编译器在 x64 上也强制使用它。GCC 选择 seh 意味着异常展开直接走 Windows 的 x64 unwind 表,性能和调试器体验都最接近原生,这是 x86_64 工具链的标准选择。
sjlj(setjmp/longjmp)最大的优势是兼容 32 位和 64 位,异常可以跨过 C 函数边界传播,但每次进入可能抛出异常的代码块都要设置跳转记录,运行时开销明显,异常发生时的调用栈也经常缺帧。这也就是社区里 x64 构建普遍使用 seh、而 32 位工具链还保留 sjlj 选项的原因。dwarf 异常在 Linux 上很成熟,但 Windows 的 ABI 不认它,mingw 的 32 位老版本用过,现在的新构建基本不在 x64 上提供。
判断异常模型也可以从gcc -v的输出里找:
gcc -v 2>&1 | grep -i exception如果看到--disable-sjlj-exceptions,就说明当前构建走的是 seh 路径;看到--enable-sjlj-exceptions则相反。在你的 CMake 项目里不需要为 seh 专门做配置,但理解这一点能帮你判断调试器里异常调用栈是否完整,也能解释为什么 -m32 模式下的行为会和 x64 模式不一样。
2.3 C 运行时:msvcrt 与 ucrt 的最大差别是部署兼容性
运行时字段 msvcrt 指链接的是微软的 MSVCRT.DLL。这个 DLL 从 Windows 2000 时代就存在,之后的每个 Windows 版本都自带,微软虽然早已停止给它加新功能,但它作为系统组件从来没有被移除过。选择 msvcrt 的最大好处是部署兼容性极强:编译出的 exe 在 Windows 7、Windows 10、Windows 11 上都能跑,不需要目标机器预装任何运行时补丁。
ucrt(Universal C Runtime)是 Windows 10 引入的新运行时,Win10 和 Win11 系统自带,但 Windows 7 和 Windows 8 默认没有,除非打系统更新补丁或手动安装对应组件。如果你的软件需要交付给老系统用户,误用 ucrt 构建会在对方机器上出现缺少 api-ms-win-crt-runtime-l1-1-0.dll这一类的启动报错。msvcrt 版本也有自己的短板,比如一些 C99/C11 的标准库函数在 msvcrt.dll 里没有对应实现,mingw 通过在头文件和库里做弥补,但行为偶尔和 Linux 下不完全一致。
三个字段的影响可以收敛成一张表:
| 字段 | 本包取值 | 常见备选 | 主要影响 |
|---|---|---|---|
| 线程模型 | posix | win32 | C++ 标准库线程/并发是否可用 |
| 异常模型 | seh | sjlj / dwarf | 异常展开性能、栈回溯质量 |
| C 运行时 | msvcrt | ucrt | 老系统部署是否需要额外运行时 |
posix + seh + msvcrt 这个组合是社区独立分发版里最常见的搭配,覆盖了"标准库完整 + x64 性能正常 + 老系统可部署"三个最实际的诉求。如果你的项目依赖 C11 新函数或者想用更现代的 UCRT,再考虑换 ucrt 构建,否则这个组合是最稳的起点。
3. 动手配置:解压、PATH、编译最小 C++ 程序的完整流程
原理清楚了,接下来就是落地。我建议你完整走一遍下面这段,很多后续的坑会在这里提前暴露。
3.1 放置路径与解压工具的选择
这个包是 .7z 格式,Windows 自带的资源管理器打不开,需要先安装 7-Zip。解压工具看起来是小事,但路径选不好,后面全是泪。我习惯把工具链放在 C 盘根目录下的纯英文短路径,比如C:\mingw64,不要放在带空格、带中文、带括号的路径里。GCC 内部很多工具通过相对路径互相查找头文件和库文件,路径一旦复杂,偶尔会解析出莫名其妙的结果。
命令行解压的写法如下:
7z x x86_64-13.2.0-release-posix-seh-msvcrt-rt_v11-rev1.7z -oC:\mingw64-o后面没有空格,直接跟目标目录。解压完成后必须检查目录结构,正常情况下C:\mingw64\bin下面应该直接有gcc.exe。如果出现C:\mingw64\x86_64-...\bin这种嵌套,说明压缩包内的根目录被整体搬了一层,GCC 的 include 路径会错位。检查命令:
ls C:/mingw64/bin/gcc.exe ls C:/mingw64/bin/mingw32-make.exe两个文件都在,说明层级是正确的。缺文件就重新解压,别急着配环境变量,这一步错了后面全是怪问题。
3.2 配置命令与 gcc 自检信息解读
工具链目录进入 PATH 后,命令行才能直接找到 gcc。Windows 下有两种常用写法,我建议用setx写用户环境变量,比改系统 PATH 更安全,不用管理员权限:
setx PATH "C:\mingw64\bin;%PATH%"setx会把当前 PATH 展开后写入用户环境变量,注意它有 1024 字符的截断上限,如果之前 PATH 已经很长,可能被切断。新开一个终端让环境变量生效,然后验证:
where gcc gcc --versiongcc --version的输出里能看到gcc.exe (MinGW-W64)和版本号 13.2.0。这里建议顺手看一下关键配置,确保你手上的构建和文件名一致:
gcc -v 2>&1 | grep -E "Thread model|enable-threads|disable-sjlj"如果Thread model: posix和--disable-sjlj-exceptions都在,说明线程和异常模型都是预想中的配置。有些发行包会在编译阶段临时调整选项,检查一次能避免后续排查时走弯路。
3.3 编译第一个多线程程序:验证 posix 线程模型
配好环境之后,我建议别急着写大工程,先编译一个带线程的 C++ 小程序,把工具的完整链路跑通。创建一个hello_thread.cpp:
#include <iostream> #include <thread> int main() { std::thread t([] { std::cout << "thread id: " << std::this_thread::get_id() << std::endl; }); t.join(); std::cout << "posix + seh + msvcrt works" << std::endl; return 0; }编译命令:
g++ -std=c++20 -O2 -Wall hello_thread.cpp -o hello_thread.exe-std=c++20不是必须的,std::thread从 C++11 开始就有,这里用 C++20 只是为了发挥 GCC 13.2.0 对现代标准支持的优势。如果这个文件在 win32 线程模型下编译,早就报std::thread不可用,你都不用猜就知道模型选错了。
运行方式取决于终端,Git Bash 里是:
./hello_thread.exePowerShell 里是:
.\hello_thread.exe程序能正常输出线程 id,说明 libstdc++ 的并发组件、运行时初始化、异常处理段都正常工作。这一步是整个工具链的"冒烟测试",过了之后再进项目级构建。
4. 配合 CMake 与 Makefile 使用:项目级构建的参数设置
命令行能编译单文件,不等于能做好项目。真实项目通常依赖 CMake 管理构建,而 CMake 在这一套工具链上的配置有几个必须注意的细节。
4.1 CMake 指定编译器路径与生成器:绕过 PATH 混乱
CMake 默认会从 PATH 里找编译器,但一台开发机上可能不止一个 GCC,比如某些嵌入式 IDE 自带的、Python 插件拉下来的,版本参差不齐。我习惯在 CMake 命令里显式指定编译器,保证任何人拉下来同一套代码,构建行为一致。先写一个最小的 CMakeLists.txt:
cmake_minimum_required(VERSION 3.20) project(mydemo LANGUAGES C CXX) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() add_executable(demo main.cpp) find_package(Threads REQUIRED) target_link_libraries(demo PRIVATE Threads::Threads)find_package(Threads)会解析当前编译器是否支持线程库,在这个工具链上它会把 winpthread 正确链进来。构建命令要指定生成器为 MinGW Makefiles,同时告诉 CMake 三个可执行文件的路径:
mkdir build cd build cmake -G "MinGW Makefiles" \ -DCMAKE_C_COMPILER="C:/mingw64/bin/gcc.exe" \ -DCMAKE_CXX_COMPILER="C:/mingw64/bin/g++.exe" \ -DCMAKE_MAKE_PROGRAM="C:/mingw64/bin/mingw32-make.exe" \ .. cmake --build . --parallel-DCMAKE_MAKE_PROGRAM必须指向这个工具链自带的mingw32-make.exe,而不是系统里其他 make。MinGW Makefiles 生成器在配置阶段会实际调用一次 make 做编译测试,找不到正确的 make 会直接报错。生成器也不要选错,Unix Makefiles 在这里是不适用的,除非你跑在 MSYS2 的 bash 环境里。
4.2 常用编译参数对二进制依赖的影响
工具链能出 exe,但 exe 能不能在目标机器上跑,取决于它依赖哪些 DLL。GCC 编译出的 Windows 程序默认依赖工具链的运行时 DLL,最常见的是libstdc++-6.dll、libgcc_s_seh-1.dll、libwinpthread-1.dll。发布时如果没有把这些 DLL 一起拷过去,对方机器就会报缺 DLL。
一组常用的部署相关参数:
| 参数 | 作用 | 对依赖的影响 |
|---|---|---|
-O2 | 开启优化 | 无 |
-s | 去除符号表 | 减小体积 |
-static-libgcc -static-libstdc++ | 静态化 GCC 的 C/C++ 运行库 | 不再依赖 libgcc_s_seh-1.dll、libstdc++-6.dll |
-static | 尽量全静态链接 | 基本不依赖外部运行时 DLL,但偶尔有坑 |
-Wl,-Bstatic,--whole-archive -lwinpthread ... | 把 winpthread 静态链入 | 消除 libwinpthread-1.dll 依赖 |
我一般只做"半静态"而不是全静态,理由放在避坑章节细说。比较稳的组合是这样:
g++ -O2 -s -std=c++20 main.cpp -o demo.exe \ -static-libgcc -static-libstdc++ \ -Wl,-Bstatic,--whole-archive -lwinpthread \ -Wl,--no-whole-archive,-Bdynamic--whole-archive保证 winpthread 里所有目标文件都被拉进来,防止符号丢失;--no-whole-archive,-Bdynamic把后续链接切回动态模式。这段参数顺序敏感,必须放在源文件之后,否则链接器按顺序扫库时可能漏掉依赖。注意,无论怎么静态化,exe 仍然会依赖系统的msvcrt.dll和KERNEL32.dll,这是正常的,别试图把系统 DLL 也打进 exe,那不是静态链接能解决的问题。
5. 这条链常见的 5 个坑与排查:从缺 DLL 到异常模型乌龙
下面是这套工具链在我这边实际项目里出现过的问题,每一条都值得你提前看一眼。
5.1 运行时报缺 libwinpthread-1.dll
现象:编译成功,但双击 exe 直接弹窗提示找不到libwinpthread-1.dll,命令行运行也一样报错。
原因:posix 线程模型默认动态链接 winpthreads,这个 DLL 在工具链的 bin 目录里,不在系统目录,exe 运行时不会自动找到它。
解决:第一,把C:\mingw64\bin加入 PATH,本机调试立刻解决;第二,发布时把这个 DLL 连同 exe 一起拷贝;第三,用前面 4.2 的半静态链接参数把它静态链进去。如果不想每次靠记忆,发布阶段可以用 6.2 的脚本统一拷贝。
5.2 系统里同时存在多个 gcc,make 用的不是这个链
现象:在项目里执行 make 或 cmake,编出来的二进制行为怪异,或者编译时用了明显的旧语法,查看版本发现 gcc 是 8.x 而不是 13.2.0。
原因:PATH 里存在多个 GCC,比如其他软件自带的编译器排在C:\mingw64\bin前面,命令解析时先命中了旧版本。
解决:排查用where gcc(cmd)或which gcc(Git Bash),看实际命中的路径。修复方式是在构建脚本里显式设置 CC、CXX 或 CMAKE_C_COMPILER,完全绕过 PATH 解析。我在自己的构建脚本里永远是显式全路径,不给环境变量留自由度。
5.3 -m32 交叉编译时异常处理模型与 seh 不符
现象:想用这套 x86_64 工具链加-m32参数编译 32 位程序,报一堆cannot find -lmingw32或fatal error: stdio.h: No such file or directory,网上搜到的方法都试了还是过不去。
原因:这是常见误解,x86_64 工具链不等于可以自由切换输出 32 位目标。GCC 的 multilib 能力在独立分发的 MinGW-w64 构建里通常没有启用,缺的就是 32 位版本的启动对象和标准库头文件。即便强行编过,异常处理模型和调试体验也会因为 seh 与 32 位目标的组合变得别扭。
解决:要做 32 位程序,直接换 i686 版本的 MinGW-w64 工具链,异常模型也要按 32 位构建去选。不要在一套链里用-m32硬凑。血泪经验:这个坑不体现在编译器的版本上,而是体现在你对工具链边界的理解上。
5.4 部署到 Windows 7 时出现运行时符号问题
现象:程序在 Windows 10 开发机上一切正常,拷贝到 Windows 7 机器上,启动报无法定位程序输入点,或者提示缺少某 dll。
原因:如果文件名的运行时字段是 ucrt,目标机器没有 Universal C Runtime;但即使运行时字段是 msvcrt(比如本包),你还需要libstdc++-6.dll、libgcc_s_seh-1.dll这些工具链运行时 DLL,它们不会自动出现在系统目录里。
解决:msvcrt 版本本身不背锅,缺的是配套 DLL 没有随程序发布。发布前用objdump -p查看依赖清单,把需要的 DLL 全部拷贝到 exe 目录。如果你需要做到"单文件部署",就用 4.2 的半静态参数,但别轻易上全静态,全静态把 msvcrt 相关对象也榨进 exe 时,某些 Windows API 相关的初始化顺序会和系统行为产生微妙差异,属于后续很难排查的玄学问题。
5.5 解压层级或中文路径导致的编译失败
现象:gcc -v 显示一切正常,但编译时头文件路径错误,比如#include <iostream>都找不到;或者 make 在项目目录里表现诡异,明明文件存在却说不存在。
原因:解压后工具链不在预期的根目录结构上,GCC 找 include 和 lib 时基于自身路径推算,目录结构不对就全部错位。另一个高发原因是路径里有中文或空格,make 对这类路径的解析在 Windows 上经常翻车。
解决:解压时直接指定-oC:\mingw64,解压后检查bin目录是否就在该路径下,不要手动把内层目录拖出来。项目路径也尽量保持纯英文,给 CI 或交付环境省去排查成本。
6. 验证工具链并做出免依赖的便携程序
最后一章不做宏大的收尾,给你一个我每次拿到新工具链都会做的验证动作,能帮你快速判断手里的版本是否可靠。
6.1 用 objdump 检查 exe 的 DLL 依赖
编译一个空程序后,用工具链自带的 objdump 查看它的依赖,这是最直观的二进制验证:
x86_64-w64-mingw32-objdump -p demo.exe | grep "DLL Name"输出里会列出 exe 引用的所有 DLL。正常情况至少会有:
DLL Name: KERNEL32.dll DLL Name: msvcrt.dll DLL Name: libstdc++-6.dll DLL Name: libgcc_s_seh-1.dllKERNEL32.dll和msvcrt.dll是 Windows 系统自带的,不用管。后面几个 lib 开头的就是工具链运行时。如果用了 posix 线程模型,还会看到libwinpthread-1.dll。看到这个列表,你对发布时要带哪些文件心里就有数了。如果目标是单文件,就按 4.2 的静态参数重编一次,再看依赖列表,会发现 lib 开头的 DLL 全部消失。
6.2 把静态链接和 dll 拷贝组合成最小发布流程
静态链接不是万能的,有些场景更推荐动态发布,比如目标机器上可能同时跑多个你开发的程序,共享同一份运行时 DLL 反而省空间。动态发布最怕漏文件,我一般在发布目录里执行一个简单的 PowerShell 拷贝:
$bin = "C:\mingw64\bin" Copy-Item "$bin\libstdc++-6.dll", ` "$bin\libgcc_s_seh-1.dll", ` "$bin\libwinpthread-1.dll" ` .\Release\拷贝前先对照objdump -p的输出,依赖里有什么就拷什么,没有的就删掉。这个检查习惯帮我避开了很多"我机器上能跑,你机器上不能跑"的尴尬。我拿到任何 .7z 结尾的 GCC 工具链,第一件事就是跑一遍gcc -v和 objdump,确认线程模型、异常处理模型、实际 DLL 依赖三者一致,再决定要不要让它进项目。这个前置检查动作看起来很慢,但比起产品上线后才发现运行时缺失,它能省下整个发布流程的返工时间。希望帮到你。
本文还有配套的精品资源,点击获取