☰
MinGW for Win64 编译避坑指南:从选型到可分发产物
2026/9/29 16:09:28 网站建设 项目流程

简介: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 / posixposix 支持 std::thread、std::mutex 等 C++11 线程设施;win32 不支持用 C++ 多线程就选 posix
异常模型seh / sjljseh 是 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 位),线程/异常模型一致。

配置步骤:

  1. 打开 Qt Creator,进入「工具 → 选项 → Kits → 编译器」,添加一个 Manual C++ 编译器,路径指向C:\msys64\ucrt64\bin\g++.exe。
  2. 在「构建套件(Kit)」里新建一个 Kit,编译器选刚加的 g++,调试器指向gdb.exe,Qt 版本选你安装的 MinGW 版 Qt。
  3. 确认 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 / msvcrtUCRT / VCRuntime
调试器gdbVisual 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 时最容易翻车的三处

  1. 编译选项不通用:-static-libgcc、-Wall这类 GCC 选项在 MSVC 下无效,要换成/MT、/W4。CMake 里用if(MINGW)/if(MSVC)分支处理。
  2. 源码里的 GCC 扩展:__attribute__、变长数组、typeof等 MSVC 不支持,需要条件编译或改写。
  3. 第三方库 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 其实是那套」。每次发布前老老实实跑一遍依赖检查,比事后被用户追着问「为什么打不开」省太多事。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询