简介:MinGW for Win64 是面向 Windows 64 位平台的 GCC 编译器工具链资源,适合在本地学习 C++、希望摆脱大型 IDE 依赖的开发者与编程初学者。它提供 GCC、libstdc++、Binutils、Make 及配套头文件与库,支持 C++11/14/17 标准及部分 C++20 特性,可命令行快速编译调试,资源占用小、启动快,编译产物跨平台兼容性较好。压缩包为 zip 格式,整体约 156.05MB,文件总数与类型明细上游暂未提供,从描述可知核心为 TDM-GCC-64 分支的 64 位工具链,包含编译器可执行文件、标准库与开发头文件等。目前已有 115 人浏览学习。借助该工具链,读者可搭建轻量级 C++ 开发环境,理解从源码到可执行文件的完整编译流程,并利用 GCC 的稳定性与标准支持完成日常练习与小型项目构建。
1. MinGW for Win64:为什么你编译出的 exe 总在别人电脑上跑不起来
你在自己机器上用 MinGW for Win64 编了个 exe,双击能跑,发给同事就报「缺少 libgcc_s_seh-1.dll」;或者你装了 Qt,选 MinGW 编译器构建一切正常,一换 MSVC 就一堆链接错误。这类问题的根子,几乎都不在代码,而在「你到底装的是哪一套 MinGW、它默认链的是哪套运行时、产物依赖了哪些 DLL」这三件事上。MinGW for Win64 说的就是 Windows 64 位平台上的 Minimalist GNU for Windows 工具链:用 GCC 系编译器产出原生 Windows 可执行文件,不依赖额外的 POSIX 模拟层。它适合三类人:想用 GCC/CMake 在 Windows 上做 C/C++ 开发的人、用 Qt 但不想被 MSVC 绑死的人、以及需要交叉编译或复现 Linux 构建脚本的人。这篇不聊虚的,从选型、安装、CMake 配置一路讲到 DLL 依赖和踩坑排查,让你把「能编」变成「编出来到处能跑」。
2. MinGW for Win64 的发行版选型:别在第一步就埋雷
MinGW 本身是一套头文件和导入库的规范,真正让你下载安装的是它的发行版。这一步选错,后面 CMake、Qt、调试全是连锁反应。很多人搜「mingw官网下载」结果下到十几年前停更的 MinGW.org 版本,或者随手装了个 32 位的,这就是血泪经验的起点。
2.1 MinGW.org、MinGW-w64、MSYS2 到底什么关系
先把三个名字理清,这是选型的地基:
- MinGW.org:最早的 MinGW 项目,只做到 32 位,更新基本停滞。现在还在用它的人,多半是照着老教程走的,不建议新项目采用。
- MinGW-w64:为解决 64 位和更多 Windows API 支持而分出来的项目,是当前 Win64 开发的事实标准。它提供两套线程模型(win32 / posix)和两套异常模型(seh / sjlj),这些参数直接决定你的程序行为和依赖。
- MSYS2:一个在 Windows 上提供类 Unix shell 和包管理器(pacman)的环境,它内部用的编译器就是 MinGW-w64。你可以把它理解成「MinGW-w64 的现代化分发 + 包管理」。
所以当有人说「我装了 MinGW」,他大概率装的是 MinGW-w64,或者通过 MSYS2 装的 MinGW-w64。选型结论很直接:新项目一律用 MinGW-w64,推荐通过 MSYS2 安装,因为包管理能帮你解决依赖、升级和版本对齐,而不是手动解压一堆压缩包。
2.2 线程模型和异常模型:两个必须现在就定的参数
这两个参数是 MinGW-w64 特有的,选错了后期很难改:
| 参数 | 可选值 | 影响 | 建议 |
|---|---|---|---|
| 线程模型 | win32 / posix | posix 支持 std::thread、std::mutex 等 C++11 线程设施;win32 不支持 | 用 C++ 多线程就选 posix |
| 异常模型 | seh / sjlj | seh 是 64 位原生异常,性能好;sjlj 靠 setjmp/longjmp,慢且 64 位下不推荐 | Win64 一律选 seh |
MSYS2 里对应的包名就能看出组合,比如mingw-w64-x86_64-gcc默认就是 posix 线程 + seh 异常。如果你手动下载压缩包,文件名里通常带posix-seh或win32-sjlj字样,认准x86_64+posix+seh这套组合基本不会错。
注意:线程模型选 win32 后,代码里用
std::thread会直接编译失败或链接报错,这不是你代码的问题,是工具链不支持。换工具链比改代码省事。
2.3 用 MSYS2 装一套干净的 MinGW-w64
下面是我一般会走的安装流程,全程命令行,可复现:
# 1. 安装 MSYS2 后,打开 MSYS2 UCRT64 或 MINGW64 终端 # 更新包数据库和基础包 pacman -Syu # 2. 如果上一步提示关闭终端重开,就重开后再执行一次 pacman -Su # 3. 安装 64 位 MinGW-w64 工具链(UCRT64 环境) pacman -S mingw-w64-ucrt-x86_64-gcc \ mingw-w64-ucrt-x86_64-gdb \ mingw-w64-ucrt-x86_64-cmake \ mingw-w64-ucrt-x86_64-ninja # 4. 验证 gcc --version g++ --version cmake --version逻辑说明:pacman -Syu先同步数据库再升级已装包,第一次跑完常要求重启终端,这是 MSYS2 的固定套路,不是出错。第 3 步一次把编译器、调试器、构建系统装齐,避免后面缺 gdb 或 cmake 再回来补。ucrt指的是使用 Universal C Runtime,是较新的运行时方案,比老的 msvcrt 更贴近现代 Windows。
参数说明:包名前缀mingw-w64-ucrt-x86_64-表示「64 位 + UCRT 运行时」的工具链。如果你在 MINGW64 环境里,前缀是mingw-w64-x86_64-,用的是 msvcrt 运行时。两者不要混装混用,否则链接阶段会出现运行时符号冲突。
装完后把C:\msys64\ucrt64\bin(或对应环境的 bin 目录)加进系统 PATH,这样在普通 CMD 或 PowerShell 里也能直接调gcc、cmake。这一步不做,后面在 IDE 里配编译器路径会反复找不到。
3. 用 CMake 驱动 MinGW for Win64:从 hello 到可分发产物
装好工具链只是开始,真正决定你能不能顺利构建的是构建系统怎么调它。CMake 与 MinGW 的组合是 Windows 上 GCC 开发最常见的搭配,但「指定编译器」和「指定生成器」这两件事经常被搞混,导致 CMake 偷偷用了别的编译器。
3.1 最小可复现工程:CMakeLists 与构建命令
先建一个最小工程,把链路跑通再谈复杂项目。
# CMakeLists.txt cmake_minimum_required(VERSION 3.20) project(hello_mingw CXX) # 强制使用 C++17,MinGW-w64 的 GCC 版本一般都能满足 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(hello main.cpp) # 关键:把 GCC 运行时静态链接进 exe,减少 DLL 依赖 if(MINGW) target_link_options(hello PRIVATE -static-libgcc -static-libstdc++ -static) endif()// main.cpp #include <iostream> #include <thread> int main() { std::cout << "MinGW for Win64 build ok\n"; // 验证 posix 线程模型是否生效 std::thread t([] { std::cout << "thread running\n"; }); t.join(); return 0; }构建命令,关键是显式指定生成器和编译器:
# 在工程目录下,用 Ninja 生成器 + 明确指定 gcc/g++ cmake -S . -B build -G Ninja \ -DCMAKE_C_COMPILER=gcc \ -DCMAKE_CXX_COMPILER=g++ \ -DCMAKE_BUILD_TYPE=Release cmake --build build逻辑说明:-G Ninja指定生成器,Ninja 比默认的 MinGW Makefiles 更快也更少路径坑。-DCMAKE_CXX_COMPILER=g++是硬性指定编译器,防止 CMake 在 PATH 里抓到 MSVC 的cl.exe或别的 gcc。-DCMAKE_BUILD_TYPE=Release决定优化级别,MinGW 单配置生成器必须显式给,否则默认无优化。
参数说明:-static-libgcc -static-libstdc++让 GCC 和 C++ 标准库静态进 exe,-static进一步把其他依赖也静态化。代价是 exe 变大,收益是换台机器不用带一堆 DLL。要不要全静态,取决于你的分发场景,见下一节。
3.2 动态链接还是静态链接:DLL 依赖的取舍
MinGW 默认是动态链接运行时的,所以你的 exe 会依赖libgcc_s_seh-1.dll、libstdc++-6.dll、libwinpthread-1.dll这几个。这就是开头那个「同事电脑跑不起来」的直接原因。
两种策略:
- 全静态:加
-static,产物自包含,分发最省心。缺点是体积大,且如果多个模块各自静态链接了运行时,跨模块传 C++ 对象(比如异常、string)可能出问题。 - 动态 + 随包 DLL:保持默认,把依赖的 DLL 和 exe 放一起。适合插件式架构或需要共享运行时的场景。
用objdump或ntldd查依赖,这是排查「缺 DLL」的后悔药:
# 查看 exe 依赖了哪些 DLL objdump -p build/hello.exe | grep "DLL Name" # 或者用 MSYS2 里的 ntldd ntldd -R build/hello.exe逻辑说明:objdump -p打印 PE 文件的所有头信息,grep "DLL Name"过滤出导入表里的 DLL 列表。ntldd -R递归解析依赖树,能看出间接依赖。发布前跑一遍,把非系统 DLL 全部随包带上,就不会再出现「我这儿能跑」的玄学。
3.3 在 Qt 里挂 MinGW 编译器:路径和 kit 怎么配
Qt 用户搜「qt 按照完 mingw 编译器后」多半卡在 kit 配置。要点是:Qt 的 MinGW 版本必须和你的 Qt 库版本匹配,位数一致(都是 64 位),线程/异常模型一致。
配置步骤:
- 打开 Qt Creator,进入「工具 → 选项 → Kits → 编译器」,添加一个 Manual C++ 编译器,路径指向
C:\msys64\ucrt64\bin\g++.exe。 - 在「构建套件(Kit)」里新建一个 Kit,编译器选刚加的 g++,调试器指向
gdb.exe,Qt 版本选你安装的 MinGW 版 Qt。 - 确认 Kit 没有黄色感叹号,否则说明某个组件路径不对。
注意:不要拿 MSVC 编译的 Qt 库去配 MinGW 编译器,ABI 不兼容,链接阶段会报一堆找不到符号。Qt 官方安装器里 MinGW 版和 MSVC 版是分开的,装哪个就用哪个编译器。
4. MinGW 与 MSVC 的差异:什么时候该换、什么时候别硬扛
搜「msvc 和 mingw 区别」的人,通常是在某个环节撞墙了。两者不是谁替代谁,而是两套 ABI 和生态。理解差异,才能判断当前项目该不该换工具链。
4.1 ABI、运行时与调试体验的实质区别
| 维度 | MinGW-w64 (GCC) | MSVC |
|---|---|---|
| C++ ABI | 自有 ABI,与 MSVC 不兼容 | 微软 ABI |
| 标准库 | libstdc++ | MSVC STL |
| 运行时 | UCRT / msvcrt | UCRT / VCRuntime |
| 调试器 | gdb | Visual Studio Debugger |
| 与 Windows SDK | 兼容但部分新 API 支持滞后 | 原生最佳 |
| 跨平台构建脚本 | 与 Linux GCC 高度一致 | 需额外适配 |
核心结论:两者编译出的 C++ 目标文件不能互相链接。你不能用 MinGW 编一个静态库,再拿 MSVC 的主程序去链。混用只在纯 C 接口(extern "C")层面才安全。
4.2 该用 MinGW 还是 MSVC 的判断清单
- 项目已有 Linux 构建脚本、大量 GCC 特有扩展:用 MinGW,迁移成本最低。
- 依赖 Windows 专有库、COM、ATL、或需要和 MSVC 编译的第三方库对接:用 MSVC。
- 用 Qt 且不想装几个 G 的 Visual Studio:用 MinGW 版 Qt,轻量。
- 需要和 Windows 驱动、系统级 API 深度交互:用 MSVC,SDK 支持更完整。
- 团队统一用 VS 生态、CI 跑在 Windows Agent 上:用 MSVC,省心。
我一般的习惯是:跨平台命令行工具、开源库复现、Qt 小工具用 MinGW;一旦涉及 Windows 平台深度集成或要链接商业 SDK,果断切 MSVC,别硬扛。
4.3 从 MinGW 切到 MSVC 时最容易翻车的三处
- 编译选项不通用:
-static-libgcc、-Wall这类 GCC 选项在 MSVC 下无效,要换成/MT、/W4。CMake 里用if(MINGW)/if(MSVC)分支处理。 - 源码里的 GCC 扩展:
__attribute__、变长数组、typeof等 MSVC 不支持,需要条件编译或改写。 - 第三方库 ABI:你依赖的库如果是 MinGW 编的,切 MSVC 后必须换成 MSVC 版,否则链接必挂。
CMake 里做编译器分支的常见写法:
if(MSVC) target_compile_options(app PRIVATE /W4 /MT) elseif(MINGW) target_compile_options(app PRIVATE -Wall -Wextra) target_link_options(app PRIVATE -static) endif()逻辑说明:用生成器表达式或条件分支,把编译器相关选项隔离,同一份 CMakeLists 就能同时支持两套工具链。这样切换时只改-DCMAKE_CXX_COMPILER,不用动工程文件。
5. MinGW for Win64 避坑排查:五个我真实踩过的坑
这一章全是现象、原因、解决三件套,都是能直接对号入座的。
5.1 编译通过但运行报缺少 libgcc_s_seh-1.dll
现象:本机双击 exe 正常,拷到别的机器报缺少libgcc_s_seh-1.dll或libstdc++-6.dll。原因:默认动态链接 GCC 运行时,目标机器没装 MinGW,自然找不到这些 DLL。解决:要么加-static-libgcc -static-libstdc++ -static全静态;要么用ntldd -R列出所有非系统 DLL,随 exe 一起打包。发布前一定在干净机器或虚拟机验证一次。
5.2 CMake 报找不到编译器或用了错误的编译器
现象:cmake -G "MinGW Makefiles"报CMAKE_C_COMPILER not found,或者构建时调用了 MSVC。原因:PATH 里同时有多个编译器,CMake 缓存了上一次的配置。解决:删掉build目录和CMakeCache.txt重新配置,并显式传-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++。切换工具链时永远清缓存,这是铁律。
5.3 std::thread 编译报错或链接失败
现象:代码里用std::thread,编译报'thread' is not a member of 'std'或链接报undefined reference to pthread_create。原因:装的是 win32 线程模型的 MinGW-w64,不支持 C++11 线程;或者没链接 pthread。解决:换 posix 线程模型的工具链(MSYS2 默认就是)。若确实要用 win32 模型,得改用 Windows 原生线程 API。链接层面加-lpthread有时能救,但根治还是换工具链。
5.4 中文路径或空格路径导致构建失败
现象:工程放在含中文或空格的目录下,make 或 gcc 报奇怪的路径错误。原因:部分构建工具和脚本对非 ASCII 路径、空格处理不完善。解决:工程路径全用英文、无空格,比如D:\work\proj。这是最省事的规避方式,别跟工具链较劲。
5.5 静态链接后异常跨模块传播崩溃
现象:主程序和 DLL 各自静态链接了 libstdc++,DLL 抛异常主程序 catch 不到,或直接崩溃。原因:两套独立的 C++ 运行时,异常对象和类型信息不共享。解决:跨模块边界要么统一动态链接同一份运行时,要么在边界处把异常转成错误码。纯 C 接口是最稳的边界设计。
6. 让 MinGW for Win64 产物真正可分发:一个发布前自检脚本
前面讲的都是「怎么编出来」,最后一章落到「怎么保证编出来的东西别人能用」。我自己的习惯是写一个发布前自检脚本,把依赖检查、静态性验证、版本信息一次性跑完,避免每次手动objdump漏看。
#!/usr/bin/env bash # release_check.sh - MinGW for Win64 产物发布前自检 set -euo pipefail EXE="${1:?用法: ./release_check.sh path/to/app.exe}" echo "== 1. 检查文件类型和架构 ==" file "$EXE" echo "== 2. 列出所有非系统 DLL 依赖 ==" # 系统 DLL 一般位于 C:\Windows\System32,这里只提示需要随包的 objdump -p "$EXE" | grep "DLL Name" | awk '{print $3}' | sort -u echo "== 3. 检查是否静态链接了 GCC 运行时 ==" if objdump -p "$EXE" | grep -qi "libgcc_s"; then echo "[警告] 仍动态依赖 libgcc_s,分发需带 DLL 或改 -static" else echo "[OK] 未发现 libgcc_s 动态依赖" fi echo "== 4. 打印编译器版本,便于归档 ==" g++ --version | head -n 1 echo "== 5. 在干净环境验证(手动步骤)==" echo "请把 exe 拷到未装 MinGW 的机器或全新虚拟机,双击运行确认"逻辑说明:脚本分五步,先确认产物架构对不对(别把 32 位当 64 位发),再列依赖,然后专门检查libgcc_s是否还在动态依赖里,接着记录编译器版本方便复现,最后提醒做干净环境验证。set -euo pipefail保证任何一步失败就停,不会带着错误继续。
参数说明:$1是 exe 路径,必填。objdump -p输出里DLL Name后面就是依赖名,awk '{print $3}'取第三列。判断静态性用grep -qi,-q静默、-i忽略大小写。这套脚本可以直接进 CI,作为发布流水线的一道卡口。
再补一个进阶技巧:如果你要同时出静态版和动态版,用 CMake 的BUILD_SHARED_LIBS加自定义 option 控制,而不是维护两份工程。构建目录分开(build-static/build-shared),配置时传不同参数,产物互不污染。
我自己这些年最大的教训就是:MinGW 的问题九成不在编译,而在「你以为它静态了其实没有」「你以为 PATH 里是这套 gcc 其实是那套」。每次发布前老老实实跑一遍依赖检查,比事后被用户追着问「为什么打不开」省太多事。希望帮到你。
本文还有配套的精品资源,点击获取