MinGW-w64 13.1.0离线安装包:Windows C/C++编译环境搭建指南
简介MinGW 64 位最新版离线安装包面向 Windows 开发者内置 GCC 13.1.0 稳定版定位为软件/插件类工具资源。包体采用 x86-64 架构基于 POSIX 线程模型启用 SEH 结构化异常处理与 UCRT 统一 C 运行时可在 64 位系统中直接编译原生 C/C 程序离线安装方式省去联网交互适合无稳定网络或需快速复现环境的使用场景。压缩包共 18725 个文件约 68.89MB除编译器、链接器与可执行程序外还包含 2441 个头文件、957 个静态库文件、990 个 Python 脚本及 3971 个说明文档便于查阅版本特性与库调用细节。对追求最新语言标准、需要稳定高效构建链的开发者而言解压后即可快速搭建本地编译环境gcc、g 等工具可直接调用也可作为长期保留的离线工具链备份。目前已有 1647 人学习下载。1. 为什么要把 MinGW-w64 13.1.0 装成离线包一件事省掉后续所有折腾拿到这个压缩包的人多半之前被在线安装器折腾过。我见过不少同事装 MinGW 装到一半网络断掉第二天重新来过更别提在线安装器动不动就卡在某个组件的下载进度条上。你要的不过是一个能用的 gcc/g 编译器放在 Windows 上把 C/C 源码编译成 exe但选择哪个版本、哪种线程模型、哪种异常处理、哪套运行时直接决定你后面调试和发布时少踩多少坑。这份 x86-64-13.1.0-release-posix-seh-ucrt-rt-v11-r 离线包把 GCC 13.1.0 的 64 位工具链完整打好了。它面向的是这一类人用 Windows 做 C/C 开发想要最新编译器特性、想要标准线程支持、想要免安装直接解压即用又不想被在线安装器反复折磨。新手能靠它把第一个程序跑起来熟手能把它当作可靠的工具链底座省去和网络、环境变量、运行时依赖作斗争的时间。下面从选型逻辑开始一步步把安装、编译、排错和进阶用法都过一遍。2. 看懂版本字符串x86-64、posix、seh、ucrt、rt-v11 分别决定了什么2.1 从一串字符里读出工具链的全部性格版本字符串里的每一项都不是随手写的。x86-64指目标架构是 64 位 x86生成的 exe 跑在 64 位 Windows 上不能用这个工具链编出 32 位程序13.1.0是 GCC 版本号属于 GCC 13 系列的第一个次版本C23 的不少特性在这个版本里已经可用posix是线程模型——这是最容易影响代码行为的一项seh是异常处理模型ucrt是 C 运行时rt-v11-r指 runtime 版本号为 v11对应 winpthreads 等运行时组件。把这串字符逐项拆开你才清楚自己拿到的是怎样一套编译器。对于大多数从 Windows 出发做 C/C 开发的人来说选型最容易交学费的地方就在posix、seh、ucrt这三项。选对了std::thread、std::mutex、结构化异常处理都能正常工作选错了编译出来的程序不是缺 dll 就是崩溃方式诡异。2.2 posix 线程模型让 std::thread 真正可用GCC 在 Windows 上常见两种线程模型win32和posix。win32 模型内部调用 Windows 线程 API行为最接近纯 Windows 风格但代价是不支持 C11 标准线程库——你用std::thread编译会直接报错。posix 模型则基于 winpthreads 库把 POSIX 线程语义映射到 Windows 上std::thread、std::async、std::future都能正常使用。如果你写的是现代 C大量依赖标准库的并发设施posix 模型就是刚需。即便你当前项目没用到线程后续接第三方库时很多库的 CMake 检测也会因为线程模型不匹配而失败。这个离线包选的就是 posix意味着你在代码里写std::thread t([]{})不会碰壁。2.3 seh 异常模型64 位下的唯一合理选择64 位 Windows 上异常处理模型只有两个选项在实战中常见seh和dwarf。seh 使用 Windows 原生的结构化异常处理机制与系统深度集成调试器能正确识别异常混合编译、跨语言调用时兼容性更好。dwarf 是基于调试信息的异常处理在某些嵌入式场景下有用但在 Windows 64 位上与系统级 API 交互时容易出现“异常无法跨模块传播”的问题。这个包选 seh 是对的。我在 64 位环境下没见过谁因为用 seh 后悔反而见过用 dwarf 的人折腾半天最后发现是第三方库内部用 seh 抛异常dwarf 模型接不住。需要明确的是seh是 64 位下的默认事实标准不是可选项里的妥协。2.4 ucrt 运行时和系统更新节奏保持一致ucrt指 Universal C RuntimeWindows 10 及以上系统自带安全更新跟着系统推送走。对比旧的msvcrt老式 C 运行时停留在上世纪ucrt 支持完整的 C99/C11 函数snprintf、strtoull这些函数都有正确实现。你编出来的 exe 只要运行在 Windows 10 上一般不需要额外携带运行时 dll。需要注意如果程序要部署到 Windows 7 这类老系统上ucrt 可能需要单独安装系统补丁但这属于个别场景。对于主流开发目标系统ucrt 是省心选项——不用像以前那样为运行时版本焦头烂额。2.5 rt-v11-r 的实践含义rt-v11-r中的 runtime 指向 winpthreads 等运行时组件的版本。不同 runtime 版本的 winpthreads 在互斥量实现、条件变量性能上有差异。v11 这个版本经过多轮迭代稳定性是足够高的。实战中你不需要记住 v11 的具体更新日志只需要知道这是配套打包好的运行时版本和 GCC 13.1.0 的 posix 模型是匹配关系。我一般建议用户直接使用发布者打包好的这个组合不要在同一个工具链里混搭不同 runtime。混搭容易出现libwinpthread-1.dll版本号对不上、编译出的程序到别的机器上缺 dll 的连锁问题。3. 部署与配置把离线包变成随时可用的编译器3.1 解压后必须完成的目录整理离线包下载后是一个压缩文件第一步是解压到你希望长期驻留的位置。不要解压到桌面临时文件夹也不要解压后随意移动目录因为后续的 PATH 配置会绑定这个路径。我之前见过有人解压到C:\Users\临时目录\Downloads\xxx清理磁盘时顺手删掉再写代码时 gcc 突然没反应了。推荐结构是把解压后的根目录放在一个没有空格、没有中文的路径下。例如C:\mingw64\ ├── bin\ ├── include\ ├── lib\ ├── libexec\ ├── share\ └── x86_64-w64-mingw32\为什么强调路径里别有空格和中文因为很多 C/C 构建脚本Makefile、CMake对路径中的空格处理有历史遗留问题哪怕现在多数工具能处理也不值得为这点事冒险。解压完成后先看一眼bin目录里有没有gcc.exe、g.exe、mingw32-make.exe这是最基本的完整性自检。3.2 环境变量 PATH 配置两种方式PATH 配置有两种常见做法推荐优先使用用户级别环境变量避免影响到系统全局配置。从控制面板进入“高级系统设置 → 环境变量”在用户变量里找到或新建Path追加一行C:\mingw64\bin还有一种方式是命令行里临时设置适合不想动全局环境的场景set PATHC:\mingw64\bin;%PATH%但这条命令只对当前终端窗口生效关掉重开就没了。所以我建议直接配用户变量一劳永逸。配置完 PATH 后新开的终端才会刷新变量已经开着的终端窗口里直接输出gcc --version往往看到的还是旧内容。验证配置是否生效gcc --version正常应该看到 gcc (GCC) 13.1.0 的版本信息输出。如果提示“不是内部或外部命令”优先检查 PATH 里追加的路径是否指向了真正的bin目录以及是否忘记重开终端。3.3 确认编译器自洽性安装完第一件事除了看版本号还可以做一次烟雾测试确认编译、链接、运行整条链路通畅。在任意目录新建一个文本文件#include stdio.h int main(void) { printf(mingw64 toolchain works\n); return 0; }编译运行gcc hello.c -o hello.exe hello.exe如果控制台输出mingw64 toolchain works说明工具链可用。这里有一个容易被忽略的细节如果你在 Windows 自带终端里运行hello.exe程序会正常输出如果你在 PowerShell 里运行可能需要加上.\hello.exe的前缀因为 PowerShell 默认不执行当前目录下的裸命令。4. 编译一个真实项目多文件、Makefile 与第三方库的接入方式4.1 从单文件到多文件项目的编译命令变化实际项目几乎不会只有一个 C 文件。假定手头有一个模拟项目目录结构长这样project_sim\ ├── main.c ├── utils.c ├── utils.h └── Makefile单文件时代的编译命令gcc main.c -o app.exe在多文件场景下需要扩展。最笨但最直观的做法是把所有源文件一次性交给 gccgcc main.c utils.c -o app.exe这种做法的优点是简单缺点是每次都要重新编译所有文件项目大了以后速度没法接受。稍微好一点的思路是分开编译再链接gcc -c main.c -o main.o gcc -c utils.c -o utils.o gcc main.o utils.o -o app.exe-c参数只编译不链接产出目标文件.o最后再统一链接。这种方式为增量编译打下基础你在改了main.c后只要重新编译main.o、再链接即可utils.o不用动。4.2 用 mingw32-make 管理构建流程mingw32-make 是 MinGW-w64 自带的 make 工具对应原生 Linux 的 make。写一个最基础的 MakefileCC gcc CFLAGS -Wall -O2 -stdc11 OBJS main.o utils.o TARGET app.exe $(TARGET): $(OBJS) $(CC) $(OBJS) -o $(TARGET) main.o: main.c utils.h $(CC) $(CFLAGS) -c main.c -o main.o utils.o: utils.c utils.h $(CC) $(CFLAGS) -c utils.c -o utils.o clean: del *.o *.exe执行mingw32-make注意 Makefile 中缩进必须用 Tab 字符不能用空格代替——这是 make 历史遗留的坑换了空格就报 “missing separator” 错误。target一节里的命令clean用的del是 Windows 命令因为 mingw32-make 在 Windows 上执行的是 cmd 命令不是 Linux 的rm。4.3 接入第三方库以静态库为例假定你拿到一个第三方库libfoo.a和头文件foo.h存放在third_party/目录下。编译时要用两个参数-I指定头文件搜索路径-L指定库文件搜索路径-l指定库名。gcc main.c -I./third_party -L./third_party -lfoo -o app.exe-lfoo让链接器去找libfoo.a静态库或libfoo.dll.a动态库的导入库。这里最容易搞错的是库文件名前缀Windows 上静态库常叫libfoo.a而 Linux 上叫libfoo.a也有但动态链接库在 MinGW 下往往是libfoo.dll.a配合foo.dll使用。如果你看到的是foo.lib这种 MSVC 风格的库文件那是给微软编译器用的MinGW 不能直接链接这里的库兼容问题后面专门说。参数位置也有讲究-lfoo必须放在源文件或目标文件之后链接器按从左到右的顺序解析符号依赖。把-lfoo放到main.c前面可能报一堆 undefined reference这是新手最容易视觉性忽略的点。5. 避坑与常见问题排查这些坑我前后踩了三次5.1 现象gcc 命令可用但系统里存在多个 MinGW 版本A同学因为项目需要装了两个 MinGW-w64 解压包一个在C:\mingw64一个在D:\tools\mingw64PATH 里两个路径都加了。结果终端里gcc --version显示的版本忽旧忽新编译行为不一致某个支持 C17 特性换一台机器就编译不过。原因PATH 中靠前的bin目录会被优先搜索两个 MinGW 都被搜索到谁在前面谁生效。解决保留一个就行。打开“环境变量”把多余的那个 MinGW 的路径从 PATH 里移除。用命令行确认当前生效的 gcc 位置where gcc只看到一个路径是健康的。看到多个路径就按上面方法清理。5.2 现象程序在开发机跑得好好的拷到别的 Windows 机器上提示缺 dll这个场景太常见了。开发机上编译完 exe打包发给同事对方双击后报“无法启动此程序因为计算机中丢失 libstdc-6.dll”。查原因前先别急着骂系统用工具看一眼 exe 依赖了什么objdump -p app.exe | grep DLL Name如果看到libstdc-6.dll、libgcc_s_seh-1.dll、libwinpthread-1.dll说明你的程序依赖了 MinGW 运行时动态库。解决发布到没有 MinGW 环境的机器时把这些 dll 一并拷到 exe 同目录或者用静态链接规避。静态链接的具体做法在下一章展开但至少要分清开发机上程序能跑不代表依赖都在这是 MinGW 动态运行时模式下的天然行为不是安装包的缺陷。5.3 现象链接时报 undefined reference to__imp_...或一堆看不懂的符号链接第三方库时报错信息里出现__imp_前缀通常是动态库导入方式不匹配。__imp_前缀表示符号来自 DLL 的导入表静态库里的符号一般不会有这个前缀。上面提到的 MSVC 生成的.lib文件给 MinGW 链接时也会出现类似问题。原因MinGW 和 MSVC 的库格式不同ABI 也不同。MSVC 的.lib是为微软链接器设计的MinGW 的链接器不认识。更隐蔽的情况是同一个第三方库提供了 MinGW 版本和 MSVC 版本你错拿了 MSVC 版本。解决优先找libfoo.dll.a或libfoo.a这种 MinGW 风格的文件。没有就自己想办法编译第三方库的源码。作为一个更省事的方案尽量选择头文件库header-only或用 CMake 重新构建第三方库。5.4 现象std::thread 编译报错提示 thread is not a member of std新手常见误配之一编译器报这个错十有八九是安装时选了 win32 线程模型而不是 posix。选 win32 模型时GCC 直接不提供标准 C11 线程库的实现。解决本离线包是 posix 线程模型安装这种版本就不会触发此问题。但如果之前装过别的版本先确认当前生效版本是不是 posix。命令行执行gcc -v看输出中Thread model一行如果写着posix则正常如果是win32要么换到 posix 版本要么在配置 Makefile 或 CMake 时接受只有 win32 线程 API 可用的事实。这类问题只会出现在系统里存在多个版本混用时。5.5 现象编译简单程序没问题一碰 C 标准新特性就报语法错误比如#include expected在新标准里明明该有的编译器却说不认识。用 GCC 13.1.0 时C23 的新特性并非全部默认开启需要显式指定标准版本。解决编译命令加参数g -stdc20 main.cpp -o app.exe g -stdc23 main.cpp -o app.exe-stdc23能启用大多数已在 GCC 13.1.0 中完成的 C23 特性。注意 GNU 扩展版本-stdgnu23会额外打开 GNU 扩展如果你需要严格遵循标准用-stdc23。6. 让编译产物更干净静态链接与发布目录的最小化6.1 静态链接的完整命令与取舍上一章提到 dll 依赖的问题这里给出可用的静态链接方案。编译时加上两个静态参数gcc main.c utils.c -o app.exe -static -static-libgcc -static-libstdc-static尽最大可能静态链接-static-libgcc强制静态链接 libgcc-static-libstdc强制静态链接 C 标准库。需要强调的是-static并不能保证所有依赖都变成静态例如某些系统库仍可能动态链接但在 MinGW-w64 场景下这套组合足以消除libstdc-6.dll、libgcc_s_seh-1.dll、libwinpthread-1.dll这几个最常见的外部依赖。静态链接的代价是 exe 体积变大一个 Hello World 可能从几十 KB 涨到 2 MB 左右。对发布到客户机器上的场景来说用体积换稳定性是完全值得的。6.2 验证发布目录是否自洽静态链接完成后再来一次依赖检查objdump -p app.exe | grep DLL Name理想情况下只剩系统自带的KERNEL32.dll、msvcrt.dll或ucrtbase.dll取决于系统这类 Windows 系统 dll。看到输出里已经没有了libwinpthread-1.dll就可以放心地把 exe 单独拷出去跑了。这里有个容易误判的点即使构建机上的 objdump 展示的依赖里有ucrtbase.dllWindows 10/11 自带的 UCRT 会兜底一般情况下不会缺。部署到 Windows 7 这种老系统时需要另外判断 UCRT 是否安装了系统补丁但那是另一码事了。6.3 日常开发里验证工具链完备性的习惯最后说一个我后来养成的工作习惯。每次拿到新版本的 MinGW-w64不管是不是这套离线包我都会写一个故意使用并发和异常特性的最小程序来验证工具链自洽#include thread #include iostream int main() { std::thread t([](int x) { std::cout thread x std::endl; }, 42); t.join(); return 0; }编译并运行g -stdc17 -pthread check.cpp -o check.exe check.exe-pthread这个参数在 Linux 上需要在部分 MinGW-w64 配置下不强制但加上它不会出错。如果这段代码能编译、运行、正常退出说明 posix 线程模型、运行时、异常处理都处于正常工作状态。从那以后我每次换工具链版本都强制走一遍这套验证流程再加新项目这十分钟省下的排查时间远比投入多。希望这篇拆解帮你在 MinGW-w64 的选型、部署和排查上少走弯路一次把工具链立稳后面写代码才痛快。本文还有配套的精品资源点击获取