简介:mingw64 是一款运行于 Windows 平台的 C/C++ 编译工具链,也是 Nuitka 打包 Python 程序时必需的底层编译器。它面向需要将 Python 工程编译为可执行文件的开发者,可解决因缺少 GCC 环境而导致的打包中断、链接报错等问题,适合中高级 Python 玩家或需要构建原生扩展的 C++ 开发者。压缩包共收录约 2000 个文件,以 C/C++ 头文件(.h/.hpp 近 1200 个)和 Python 脚本(.py 约 760 个)为主体,并含有少量说明文档与配置文本,总大小约 94MB;头文件覆盖 SQLite、AVX512 指令集等常见库与硬件特性,可在多种编译场景下直接使用。目前已有 945 人学习下载。解压并配置好环境后,即可获得一套完整可用的 mingw-w64 工具链,其中包含 GCC 编译器、标准库头文件以及 Nuitka 所需的辅助脚本,省去自行下载多个依赖的繁琐过程,帮助开发者快速完成 Python 程序的 C 语言级打包发布。
1. mingw64 压缩包:不是安装程序,是一整套 GNU 工具链
你从网上下载了一个 mingw64(x86-64-15.1.0-release-win32-seh-ucrt-rt-v12-rev0.7z),双击打不开,右键也没有“安装”按钮,甚至有些杀毒软件还会提示文件过大。别急着删,它本来就不是安装程序,而是一个免安装的绿色压缩包:里面是 GCC 15.1.0 在 Windows x64 平台上的完整移植,包含 gcc、g++、gdb、mingw32-make,以及配套的头文件、静态库和导入库。解压配好 PATH,它就能帮你脱离 Visual Studio,独立编译、调试和打包 C/C++ 项目。适合两类人:一类想在 Windows 上跑通开源项目、CMake 构建流程的开发者,另一类是被 MSVC 兼容性问题折磨、需要 GCC 系工具链的跨平台从业者。你要做的不是双击运行它,而是把它当作一个 SDK 来用。
2. 后缀拆解:x86-64、15.1.0、win32、seh、ucrt 分别决定了什么
文件名越长,信息量越大。这个包里每一个短横线分隔的片段,都是一次明确的技术选型。改掉其中一个字母,工具链的行为就可能完全不同。第 2 章先把这些后缀逐一拆开,搞清楚“这个包到底能用在哪、不能用在哪”。
2.1 x86-64 与 15.1.0:目标架构和编译器版本
x86-64 是目标架构。它决定了你编译出来的 exe 是 64 位 PE32+ 格式,只能在 64 位 Windows 上运行。如果你在 32 位机器上跑,会直接报“不是有效的 Win32 应用程序”。与之对应的是 i686 或 x86,那些是 32 位工具链,日常开发已经不推荐了。判断一个包是不是 x86-64,不能只看文件名,还要在编译后用 objdump 确认,后面第 3 章会演示。
15.1.0 是 GCC 版本号。GCC 15 是 2025 年的主线正式版本,15.1.0 是这条主线上的第一个发布版。它最直观的变化是把默认语言标准从 gnu17 改成了 gnu23。这意味着你用 C23 的新语法,比如二进制字面量0b1010、_BitInt大整数类型、不带头文件的static_assert,在 GCC 15 里不需要加任何参数就能编译;换成 GCC 12 或更早,编译会直接拒绝。对 C++ 项目来说,GCC 15 对 C++23 标准的覆盖也更完整,写std::expected这类东西时更省心。编译器版本的迭代不只是“数字变大”,它直接决定你能用哪一代语言特性。
这里有个常见误解:15.1.0 不是 Windows 版本,也不是 MinGW-w64 自身的版本,它是 GCC 编译器的版本。MinGW-w64 是“把 GCC 移植到 Windows”的这个项目,它的运行库版本由rt-v12-rev0表示,后面 2.3 会讲。
2.2 win32 与 seh:线程模型和异常处理模型
win32是这个包的线程模型。MinGW-w64 的下载页上通常给两个选择:win32 和 posix。win32 线程模型直接用 Windows 的线程 API,不依赖winpthreads.dll,运行时更干净,适合写纯 C 或者用 Qt、wxWidgets 这类自带线程封装的项目。posix 线程模型通过 winpthreads 在 Windows 上模拟 POSIX 线程接口,代价是多一个动态库要分发,但换来的是std::thread、std::mutex这些 C++ 标准线程库可以正常使用。
如果你写的代码里用了std::thread,选 win32 线程模型的包会编译失败或链接失败。这不是工具链坏,是它本身做了取舍。我在 4.3 会专门展开这个问题。
seh是异常处理模型,对应 GCC 在 Windows 上的三种实现之一:seh、dwarf、sjlj。SEH 是 Windows 原生结构化异常处理,64 位下的 GCC 用它来展开 C++ 异常。它的优势是和 Windows x64 的异常表格式一致,调试器能正确解析堆栈,和 MSVC 编译出来的模块互相调用时异常边界也更好处理。DWARF 是 GCC 传统的异常模型,在 32 位工具链里更常见,64 位下使用会带来额外的运行时开销,调试体验也差一截。这里给一个简洁的对比:
| 对比项 | SEH | DWARF |
|---|---|---|
| 64 位支持 | 原生支持 | 可用但依赖 .eh_frame |
| 与 Windows 调试器兼容 | 好 | 一般 |
| 堆栈展开性能 | 更好 | 开销更大 |
| 常见使用范围 | x86_64 包 | i686 包 |
把x86-64 + win32 + seh + ucrt连起来看,这是一个“Windows 64 位 + 原生线程 + 原生异常 + 现代运行库”的组合,在 Windows 10/11 上运行最干净,产出的 exe 对系统 API 的依赖最直接。
2.3 ucrt 与 rt-v12-rev0:运行库和 ABI 基线
ucrt指的是 Universal C Runtime,也就是ucrtbase.dll。Windows 10 开始,这个 DLL 是系统组件,开机就有。所以用 UCRT 版本工具链编出来的程序,在 Windows 10/11 上分发时不需要额外带 C 运行库,这是它最大的优势。对比之下,老式的 MSVCRT 版本工具链链接的是msvcrt.dll,那个 DLL 虽然是系统自带的,但对 C99/C11 标准库的支持残缺,printf族函数的行为也和标准有明显的出入。简单对比一下:
| 对比项 | UCRT | MSVCRT |
|---|---|---|
| Windows 10/11 自带 | 是 | 是 |
| Windows 7 | 需安装补丁 | 原生 |
| C99/C11 支持 | 较完整 | 残缺 |
| 标准库行为 | 接近 glibc | 老旧 |
选择 UCRT 版的代价是目标系统如果是 Windows 7 及更老,需要提前装 KB2999226 补丁,否则程序启动时找不到入口点。现在做产品还死守 Win7 的团队不多,但如果你就是那一个,记得第 4.4 的坑。
rt-v12-rev0是 MinGW-w64 运行库的版本号,rev0 表示第一次修订。它决定了libgcc、libstdc++、libmingwex这批库的 ABI 基线。同一个小版本的 GCC,配不同 rev 的运行库,库的导出符号可能有细微差别。我的经验是:升级工具链时,bin、lib、include三个目录必须整包替换,不要只换一个gcc.exe或单独覆盖libstdc++.dll,否则很容易出现“编译过了但运行崩溃”的诡异问题。这类问题最难排查,因为报错信息往往和实际原因完全无关。
3. 落地三步:解压、配 PATH、编译出第一个 64 位 exe
理论部分讲完,现在开始真正落地。整个过程说起来只有三步:解压到目标目录、把 bin 写进 PATH、编译一个 Hello World。但每一步都有细节,做错了会在后面某个环节翻车。
3.1 解压到纯英文且无空格的路径
先准备一个干净目录。我一般用C:\mingw64,或者C:\dev\mingw64,不建议放到C:\Program Files下,原因后面说。假设你已经有了 7-Zip,命令行解压命令如下:
mkdir C:\mingw64 7z x "mingw64(x86-64-15.1.0-release-win32-seh-ucrt-rt-v12-rev0.7z)" -oC:\mingw64 -y命令里的-o开关指定输出目录,注意-o和路径之间没有空格,这是 7-Zip 的固定语法。-y表示遇到同名文件自动确认覆盖。文件名里的括号在 Windows 命令行里不是特殊字符,但为了安全,整个文件名我用引号包起来了。如果不用命令行,右键“解压到 mingw64(x86-64-15.1.0-release-win32-seh-ucrt-rt-v12-rev0)”也能解,但容易解出一层嵌套目录,导致后面找不到gcc.exe。
解压完成后检查目录结构。正常的布局是:
C:\mingw64\ bin\ gcc.exe、g++.exe、gdb.exe、mingw32-make.exe、objdump.exe include\ C/C++ 头文件 lib\ libgcc.a、libstdc++.a、libmingwex.a 等如果你的实际路径是C:\mingw64\mingw64\bin\gcc.exe,说明解压层级多了一层。不用重解,直接把路径记成C:\mingw64\mingw64也能用,但没必要给自己增加记忆负担,重解一次更干净。
提示:路径里的空格是后面所有坑的根源。空格会让 make、cmake、shell 脚本在拼接路径时出现奇怪问题,而且报错往往指向头文件找不到,让人误判成工具链坏了。
3.2 配置 PATH:让终端能找到 gcc
PATH 配置有几种方式,最省事的是图形界面:右键“此电脑 → 属性 → 高级系统设置 → 环境变量”,在用户变量里找到 Path,新增一行C:\mingw64\bin。但我在命令行里操作更顺手,cmd 下可以用:
setx PATH "%PATH%;C:\mingw64\bin"这个命令有个隐患:setx对变量长度有 1024 个字符的限制,如果当前 PATH 已经很长,写入时会被截断,轻则 PATH 丢东西,重则 cmd 都打不开。所以我更推荐在 PowerShell 里用 .NET API 来写:
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\mingw64\bin", "User")参数User表示写入用户级环境变量,不需要管理员权限。这个写法绕开了setx的长度限制,也不会污染系统级 PATH。配置完之后,当前终端不会立即生效,需要新开一个 cmd 或 PowerShell 窗口。验证是否成功:
where gcc gcc -vwhere gcc能找到C:\mingw64\bin\gcc.exe,说明 PATH 生效;gcc -v会打印版本详情,里面有一行Thread model: win32,可以确认线程模型确实是 win32。
3.3 编译第一个程序并确认位数
在C:\dev\hello或任意工作目录下新建hello.c:
#include <stdio.h> int main(void) { printf("hello from mingw64\n"); return 0; }编译命令:
cd C:\dev\hello gcc -Wall -Wextra -O2 -std=gnu23 -o hello.exe hello.c .\hello.exe参数说明:-Wall开启常见警告,-Wextra开启更细致的警告,-O2是常用优化等级,-std=gnu23指定语言标准为 C23(GCC 15 默认就是 gnu23,显式写出来是为了让构建日志里的意图更明确)。-o指定输出文件名,在 Windows 上必须带.exe后缀,否则系统不认。cmd 里执行.\\hello.exe会报错,直接敲hello.exe就行;PowerShell 里用.\hello.exe。两种终端都支持的话,用 PowerShell 更顺手。
编译成功后,验证一下产物确实是 64 位:
objdump -f hello.exe | grep "architecture"在纯 cmd 环境里没有 grep,把命令换成objdump -f hello.exe | findstr architecture。PowerShell 用objdump -f hello.exe | Select-String architecture。输出里应该能看到architecture: i386:x86-64,这就确认了工具链按 x86-64 目标工作。
再用 C++ 试一遍。新建hello.cpp:
#include <iostream> int main() { std::cout << "cpp works\n"; return 0; }编译命令类似,把gcc换成g++,标准换成 C++23:
g++ -std=c++23 -Wall -Wextra -O2 -o hello_cpp.exe hello.cpp .\hello_cpp.exe这里注意,gcc和g++在 MinGW-w64 里是两个独立的可执行文件,别指望用gcc编译.cpp能自动链接 C++ 标准库,老老实实用g++。
3.4 让 IDE 识别这个工具链
VS Code 用户装了 C/C++ 扩展之后,在.vscode/settings.json里指定编译器路径:
{ "C_Cpp.default.compilerPath": "C:\\mingw64\\bin\\gcc.exe", "C_Cpp.default.cppStandard": "c++17" }CLion 用户在Settings → Toolchains里点加号,选 MinGW,把Toolchain路径填成C:\mingw64,它会自动识别出gcc.exe、g++.exe和gdb.exe。注意 IDE 有时会从桌面图标启动,不会继承你在终端里设置的 PATH,所以 compilerPath 尽量写绝对路径,不要依赖 PATH 推导。
4. 避坑:MinGW-w64 落地最常见的 5 个翻车位
同样一个包,有些人十分钟跑通,有些人折腾一整天。下面这几条是我在 Windows 上配 MinGW-w64 时遇到的真实问题,每一条都按“现象 → 原因 → 解决”写清楚。
4.1 编译成功,运行时报“找不到 libgcc_s_seh-1.dll”
现象:gcc编译没有任何报错,双击 exe 弹出“由于找不到 libgcc_s_seh-1.dll,无法继续执行代码”。原因:默认情况下 exe 动态链接了libgcc_s_seh-1.dll,这个 DLL 在C:\mingw64\bin下。如果你从图形界面双击 exe,系统只会去 PATH 找 DLL;如果 bin 目录没写进系统 PATH,就会报这个错。解决:把C:\mingw64\bin写进系统环境变量而不是只写在某个终端里;或者编译时加静态链接参数,让运行库直接嵌入 exe:
gcc -O2 -static -static-libgcc -static-libstdc++ -o hello.exe hello.c临时应急可以把 DLL 复制到 exe 旁边,但不推荐:DLL 版本和工具链版本绑定,工具链升级后拷贝过来的旧 DLL 会造成更深层的问题。注意报错的 DLL 文件名里带seh,如果你的工具链是 DWARF 模型,缺的是libgcc_s_dw2-1.dll,别混着下。
4.2 编译报错“cannot open output file a.exe:Permission denied”
现象:在项目目录执行gcc,编译过程无报错,但输出阶段提示没有权限写a.exe。原因:Windows 安全软件对当前目录有写保护,或者工作目录在C:\Program Files这类受系统管控的路径下,当前终端的权限不够。解决:在普通用户目录下新建工作目录,比如C:\dev\project,把源码放进去再编译。这不是编译器坏,是目录权限问题。
提示:如果目录里有同名的
a.exe正在被另一个进程运行,Windows 也会报 Permission denied。先检查任务管理器里有没有残留进程,再怀疑权限。
4.3 用了 std::thread 编译失败,报错里没有具体原因
现象:代码里写了std::thread或std::mutex,g++报错信息指向“this file requires compiler and library support for the ... which does not provide”之类。原因:这个包的线程模型是 win32,没有winpthreads支撑,C++ 标准线程库不可用。解决:如果是不依赖线程的项目,改用 Windows APICreateThread;如果代码里到处都是std::thread,换 posix 线程模型版本的 MinGW-w64 包。win32 线程模型面向的是 C 项目或自带线程封装框架的场景,选错了不是你写代码的问题,是选型问题。
4.4 程序在 Windows 7 上运行弹窗“无法定位程序输入点”
现象:exe 在自己的 Windows 10 机器上跑得好好的,拷贝到 Windows 7 上打不开,弹窗提示找不到某个函数入口。原因:UCRT 在 Windows 7 不是系统组件,目标机器上没有ucrtbase.dll。解决:给目标机器装 KB2999226 补丁;或者改用 MSVCRT 版本的 MinGW-w64 包重编。不建议把ucrtbase.dll复制到 exe 旁边,Windows 对系统 DLL 的加载优先级有特殊规则,复制文件经常不生效,还会掩盖真实问题。
4.5 在 VS Code 里选文件夹时报 directory picker failed
现象:配置 C/C++ 扩展的工作目录,或点“选择文件夹”按钮时,弹窗提示directory picker failed: win32 folder dialog failed,按钮完全点不动。原因:Windows 文件夹选择器 COM 组件在当前进程里异常,常见于从桌面快捷方式启动的 VS Code 进程拿到了不完整的外壳环境。解决:不走图形按钮,直接编辑.vscode/settings.json,手写路径:
{ "C_Cpp.default.compilerPath": "C:\\mingw64\\bin\\gcc.exe" }然后在终端里用cd C:\dev\project进入项目目录,再用code .启动 VS Code,让进程继承终端的用户环境变量。这个报错本身不是 MinGW-w64 的问题,但很多人配第一个 C++ 工程时碰到,误以为是编译器没装好。
4.6 读取源码文件报 SetNamedSecurityInfoW Failed
现象:用脚本批量扫描源码目录,或者 IDE 索引文件时,报权限错误,日志里有setnamedsecurityinfow failed字样。原因:文件或目录的 ACL 拒绝当前用户读取安全描述符,常见于从别人机器拷贝来的源码目录,或者被安全软件打过 ACL 标记。解决:右键目录 → 属性 → 安全 → 编辑,给当前用户完全控制权限;或者把工程目录挪到C:\dev这种干净路径下。别用管理员终端强行提权,能跑但治标不治本,换个目录还是复现。
5. 进阶:动态/静态链接怎么选,CMake 怎么对接它
跑通 Hello World 之后,真正干活会遇到两个更现实的问题:编译出来的 exe 怎么给别人分发,以及怎么和 CMake 构建体系配合。这两个问题决定了你能不能把这个工具链用到实际项目里。
5.1 动态链接和静态链接的取舍
MinGW-w64 默认是动态链接,编译出的 exe 依赖bin目录下的几个 DLL:libgcc_s_seh-1.dll、libstdc++-6.dll,某些场景还有libwinpthread-1.dll。exe 体积小,但分发时必须带上 DLL。看一个 exe 依赖哪些 DLL,用这个命令:
objdump -p hello_dyn.exe | grep "DLL Name"cmd 下把grep换成findstr。输出里会列出KERNEL32.dll、msvcrt.dll或ucrtbase.dll,以及libgcc_s_seh-1.dll和libstdc++-6.dll。
如果不想带 DLL,编译时加静态链接参数:
g++ -O2 -static -static-libgcc -static-libstdc++ -o hello_static.exe hello.cpp-static告诉链接器尽量把所有库静态链进 exe,-static-libgcc和-static-libstdc++则只针对 GCC 运行库做静态化。区别在于:只写-static会把第三方库也一并静态化,体积膨胀明显;后两个参数只处理 GCC 自带的库,是更可控的做法。静态链接后 exe 体积会从几十 KB 涨到几百 KB 甚至上 MB,但换来的是不需要在目标机器上配任何环境变量。
| 链接方式 | 体积 | 分发复杂度 | 适用场景 |
|---|---|---|---|
| 动态 | 小 | 需带 bin 下 DLL | 产品模块、频繁更新 |
| 静态 | 大 | 免 DLL | 内部工具、演示程序 |
我的习惯是:给别人演示的小工具用静态,正式项目用动态链接加上完整的 DLL 清单。动态链接不是坏事,它让公共库可以被多个 exe 共享,更新时只要替换一部分模块,代价是打包要做仔细。
5.2 用 CMake 和 MinGW Makefiles 把项目 build 起来
MinGW-w64 自带的构建工具叫mingw32-make.exe,它在bin目录下。CMake 要选对生成器才能和它配合。先看一个最简单的CMakeLists.txt:
cmake_minimum_required(VERSION 3.20) project(hello_min LANGUAGES C CXX) add_executable(app main.cpp)生成构建系统并编译:
cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -S . -B build cmake --build build build\app.exe-G "MinGW Makefiles"是这里的关键参数。不指定生成器时,CMake 在 Windows 上默认用 Visual Studio 生成器,产出.sln工程文件,那套东西和gcc完全没有关系。指定MinGW Makefiles之后,CMake 会生成给mingw32-make用的 Makefile。这里有个小坑:MinGW 的 make 叫mingw32-make,不是make。如果你在终端里敲make找不到命令,不是没装,是名字不一样。
还有一个更隐蔽的坑:如果选择了Unix Makefiles生成器,CMake 会去找sh.exe,而 Windows 上默认没有这个文件,报错信息是sh.exe was not found in PATH。看到这行字,第一反应不是去装 sh,而是检查是不是生成器选错了。Windows 上永远选MinGW Makefiles或Ninja。Ninja 的速度更快,但需要另外准备ninja.exe,这里不展开。
CLion 用户不用敲命令行,直接在 Toolchain 设置里选 MinGW,CMake 生成器会自动用MinGW Makefiles。VS Code 用户配上 CMake Tools 扩展后在设置里把cmake.generator改成MinGW Makefiles,效果一样。
6. 验证工具链:一条命令确认 SEH 与 UCRT 没搞错
拿到任何 MinGW-w64 包,我做的第一件事不是编译 Hello World,而是确认这个包的实际配置和文件名描述一致。文件名可以造假,配置信息做不了假。最直接的一招是看依赖:
objdump -p hello_dyn.exe | grep "DLL Name"记得先在 5.1 用动态链接方式编一个hello_dyn.exe,然后看输出。如果工具链确实是 SEH 模型,依赖里一定能看到libgcc_s_seh-1.dll,文件名里就带着seh;如果是 DWARF 工具链,这里会出现libgcc_s_dw2-1.dll。同理,ucrtbase.dll出现说明运行库是 UCRT,如果看到msvcrt.dll,说明这个包是老式的 MSVCRT 构建。这一条命令同时验证了两个最核心的选型,非常划算。
第二件事是确认编译器版本和线程模型,跑gcc -v。输出末尾你会看到Thread model: win32,这就是当前包的真实线程模型,和文件名对上了。如果有朝一日你下载了 posix 版本,这里会显示Thread model: posix。
第三件事是验证目标架构,回到第 3 章里的objdump -f hello.exe | findstr architecture。看到i386:x86-64就放心了,这个包不会给你编出 32 位 exe。
最后分享一个习惯:我第一次配 MinGW-w64 时,把 7z 包解压到了带空格的路径下,然后折腾了一整天,报错千奇百怪,最后把目录挪到C:\mingw64就全好了。后来每次拿到新工具链包,我都先跑一遍这三条验证命令再动手,省掉很多无用功。希望帮到你。
本文还有配套的精品资源,点击获取