十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MinGW-w64 gcc 4.9.2 安装与编译实践:从DLL到SDL的完整指南

MinGW-w64 gcc 4.9.2 安装与编译实践:从DLL到SDL的完整指南 简介面向Windows平台开发者的六十四位GNU工具链资源采用MinGW-w64四点九点二版本内置GCC四点九点二编译器可直接生成原生六十四位C语言与C加加程序适合搭建轻量级命令行编译环境、学习GCC工作流程或迁移Linux及Unix开源项目。压缩包共三千五百四十一个文件大小约三十五兆字节以头文件、静态库、可执行工具和目标文件为主覆盖标准库声明、链接支撑与编译驱动解压配置环境变量后即可在命令行完成编译、链接与构建。目前已有约一千五百零八人学习下载。与完整IDE相比这套工具链更精简支持离线部署内置POSIX线程模型可较好支撑六十四位多线程应用配合GDB调试器与Makefile脚本能完成从源码到调试运行的完整流程适合系统编程、软件移植和编译技术学习。 很多老 Windows 程序员对 MinGW 的感情是又爱又恨。爱的是它把 GCC 带到了 Windows 下写 C/C 不用再被 Visual Studio 拖着重重的工程文件跑恨的是版本号一停就是好几年——尤其是标题里这个 gcc 4.9.2拿到手一看会恍惚觉得自己穿越回了 2014 年。但这并不代表它没用不少嵌入式 IDE、老 SDK、第三方预编译库到今天还在指定用它新版编译器一上就链接报错。这篇我就围绕 MinGW 64 位下的 gcc 4.9.2把它到底是什么、怎么装才不踩旧版本优先级坑、64 位下编译 DLL 和链接 SDL 这类库的具体步骤、以及在 WSL/Linux 上绕不过去的编译器版本问题一次讲清楚。后面写到的都是我在实际项目里踩过、验过的东西新手可以直接抄作业老手也可以看看有没有漏掉某些细节。1. 先弄清楚这个4.9.2到底是谁家的编译器1.1 MinGW 和 MinGW-w64 不能混着叫很多人把 MinGW 和 MinGW-w64 当成同一个东西实际上这是两个项目。老 MinGWmingw.org 那个长期停留在 32 位工具链版本也一直压着 gcc 4.8、4.9 不放64 位的 Windows 移植工作基本是靠 MinGW-w64 这个分支在维护。所以“MinGW 64位 gcc 4.9.2”这个说法更准确的表述是MinGW-w64 工具链中的一个经典预编译版本。如果你去下载会看到类似x86_64-4.9.2-posix-seh-rt_v3-rev1这样的压缩包名。这串字符里有两个关键信息posix代表线程模型是 POSIXseh代表 64 位下的异常处理模型。装完后执行gcc -v输出里Target: x86_64-w64-mingw32和Thread model: posix这两行就说明工具链架构和线程模型都对了。建议直接去官方发布页下载不要从第三方下载站拿否则很容易拿到被改动过或捆绑其他东西的版本。1.2 4.9.2 对 C 标准支持到什么程度这个版本放到今天看标准支持能力就是“够用但别贪”。我整理了一个简单的参照表标准支持情况备注C98 / C03完整老项目最稳的选择C11大部分特性可用lambda、auto、右值引用、可变参数模板基本稳定C14部分支持需要-stdc14或-stdc1y不是完整实现C17基本不支持别指望用它写现代 C特别提一下 C11 的std::regex在 gcc 4.9.x 上是出了名的慢且行为怪异如果你写的是正则相关逻辑建议绕开或者干脆自己写简单的字符串匹配。C14 里的泛型 lambda、返回类型推导这类特性4.9.2 实现得很勉强容易踩到编译器内部错误。所以如果你是新项目、没有历史包袱我不建议主动选 4.9.2但如果你被老依赖绑住了那就要学会和它的边界共存。1.3 到底哪些项目还死守这个版本我见过最典型的几类一类是嵌入式 IDE 的插件编译环境比如 S32 Design Studio 这类工具内部自带了一套 gcc 4.9.x 工具链有些人尝试装 gcc 10.2 插件进去结果直接编译失败因为这些 IDE 的 Makefile 脚本和 SDK 头文件是按老编译器设计的不是版本越新越好。另一类是老的驱动 SDK 或第三方闭源库发布方只提供了针对 gcc 4.x ABI 编译好的静态库你升到新编译器去链接符号对不上报错满屏。还有一类是学校实验课和竞赛环境课件、评分脚本都基于老版本学生自己升级编译器反而跑不出预期结果。看到这里你应该明白死守 4.9.2 的人不是落后是被生态绑住了。2. 下载、装完、确认版本三步里每一步都有坑2.1 下载路径和安装目录的选择下载 MinGW-w64 的预编译包时建议选一个纯英文路径解压比如C:\mingw492。不要放进C:\Program Files这种带空格的目录否则一些 Makefile、批处理和 IDE 在解析路径时会出现诡异问题。这个问题在 4.9.2 时代特别明显因为很多老脚本根本没有做路径转义。解压后把C:\mingw492\bin加进系统环境变量 PATH。这里的bin目录里能直接看到gcc.exe、g.exe、mingw32-make.exe这些可执行文件。顺便说一句如果你只是想要一个能编译 Windows 程序的工具链MSYS2 现在也是很好的选择但它默认的 gcc 已经是 10 版本和 4.9.2 不是一回事。两个环境可以共存做法就是各自独立目录、手动控制 PATH 优先级。2.2 “升级后还是旧版本”的完整排查链路这个坑几乎每个玩 MinGW 的人都踩过而且搜索量常年居高不下明明装好了新版 gcc一敲gcc --version显示的却还是旧版本。原因几乎都是 PATH 优先级和进程缓存问题不是安装坏了。排查链路我给你列全重新打开一个 cmd 窗口输入where gcc看输出的路径顺序。Windows 按 PATH 里的先后顺序查找排在最前面的生效。打开“系统属性 - 环境变量”检查系统和用户 PATH 里是否同时存在多个编译器路径比如老 MinGW 的C:\MinGW\bin和新版的C:\mingw492\bin。把新版bin路径移动到旧版之前或者直接删掉旧版路径。改完一定要重开终端不能复用已经打开的窗口因为进程环境变量在启动时就固定了。如果用的是 Qt Creator、VS 这类 IDE还要去构建套件设置里同步工具链路径IDE 会缓存自己的 PATH 快照。WSL 里也有类似的“升级后还是旧版本”问题但机制不同。which -a gcc可以列出所有候选路径然后你要看/usr/local/bin和/usr/bin哪个排在前面如果 shell 记住了旧命令的 hash 缓存执行hash -r刷新一下就好。很多人装完新版 gcc 没刷新 shell 缓存结果以为自己装失败了其实只是还没生效。2.3 WSL 里想用 gcc 开发先搞清楚在给谁编译最近“如何在 WSL 中设置安装 gcc 开发环境”这个问题特别多。核心认知是WSL 里的 gcc 是 Linux 编译器编译出来的是 Linux ELF 文件不能在 Windows 下直接运行而 Windows 侧安装的 MinGW gcc 编译出来的才是 Windows PE 可执行文件。两者不是同一个东西也不能混着用。如果你希望在 WSL 里写代码、但最终要在 Windows 下运行程序有两种做法一种是在 WSL 里安装交叉编译器gcc-mingw-w64-x86-64然后用x86_64-w64-mingw32-gcc编译出 Windows exe另一种是直接在 WSL 里用apt install build-essential装 Linux 版本 gcc用来跑 Linux 程序。这套逻辑理清楚就不会整天被 WSL 和 MinGW 之间的路径问题搞晕了。3. 64位Windows下用4.9.2编译DLL和SDL工程3.1 从源码编译动态库导出符号和导入库Windows 下用 MinGW 编译 DLL和 Linux 下编 .so 思路相似但多了导出符号和导入库这两件事。先看一个最简单的例子假设有个mylib.c__declspec(dllexport) int add(int a, int b) { return a b; }编译命令gcc -shared -o mylib.dll mylib.c -Wl,--out-implib,libmylib.dll.a-shared表示生成动态库-Wl,--out-implib,libmylib.dll.a会在生成 DLL 的同时生成一个导入库文件。导入库的作用是给调用方链接时用的将来编译main.c时extern __declspec(dllimport) int add(int, int); int main(void) { return add(2, 3); }然后执行gcc main.c -L. -lmylib -o main.exe-lmylib链接的就是刚才生成的libmylib.dll.a。很多人第一次编译 DLL 会忘记生成导入库导致调用方链接时找不到符号报一堆 undefined reference。64 位下不用像 32 位那样纠结__stdcall导出名的修饰问题但导出的 C 函数建议加extern C或者单独用 .def 文件否则导出名会被 name mangling 搞乱。3.2 链接 SDL 这类第三方库时的架构匹配问题“gcc link sdl”这个搜索词背后多半是在编译 SDL2 程序时遇到了链接错误。最常见的根源只有一个库文件版本和编译器架构不匹配。SDL 官方会提供几种开发包其中带mingw字样的才是给 MinGW 用的比如SDL2-devel-2.0.x-mingw.tar.gzMSVC 版库给 Visual Studio 用MinGW 的 gcc 没法直接链接 MSVC 的 .lib 文件虽然理论上能转但非常麻烦不建议折腾。另外还要看清楚是x86_64还是i68664 位 gcc 必须配对 64 位库否则会报“file not recognized: File format not recognized”。下载好开发包后把解压出来的 include 和 lib 目录整理到固定位置比如D:\SDL2编译命令大致是gcc main.c -I D:/SDL2/include/SDL2 -L D:/SDL2/lib -lmingw32 -lSDL2main -lSDL2 -mwindows -o game.exe这里-lmingw32 -lSDL2main -lSDL2的顺序是官方推荐的不能随便调换。如果你用了main函数但没链接SDL2main会遇到经典的undefined reference to SDL_main报错不要慌就是入口函数约定问题。运行程序时记得把SDL2.dll放到 exe 同目录或者加到 PATH 里。3.3 4.9.2 的多线程和 libstdc 运行时MinGW-w64 的 gcc 在构建时有两种线程模型posix和win32。posix模型下的 4.9.2std::thread、std::mutex能用底层靠的是 winpthreads 库如果是win32线程模型用 STL 线程类写出来的程序轻则编译报警告重则运行崩溃。判断方式很简单跑一条gcc -v看Thread model那一行确认。另外动态链接 libstdc 的程序发布时需要一起带上libstdc-6.dll、libgcc_s_seh-1.dll、libwinpthread-1.dll这几个运行时 DLL否则目标机器上会提示找不到依赖。如果不想散落一堆 DLL编译时加-static-libgcc -static-libstdc可以静态链接代价是 exe 体积变大。对这个老编译器来说我建议发布时优先选择静态链接减少运行时环境差异带来的排障成本。4. 从WSL到CentOS老环境里折腾GCC版本的真实体会4.1 升级完调用到的还是老编译器在 WSL 或 CentOS 7 这类老系统上升级 gcc 后输入gcc --version还是旧版本原因和 Windows 侧极为相似PATH 优先级和 shell 缓存。Linux 下推荐用几条命令排查which -a gcc type -a gccwhich -a列出 PATH 里所有候选路径type -a显示 shell 实际会调用哪一个二者顺序不完全一致时多半是 shell 内建命令或 alias 在捣乱。如果你用 devtoolset 或者手动编译安装到/usr/local/bin而系统自带的旧版本在/usr/bin默认 PATH 一般把/usr/bin排在前面新版本自然不会被调用。解决方式可以是改 PATH 顺序也可以用update-alternatives统一管理。顺带回答一下“CentOS 7 上手动升级 gcc 到 gcc 8.3.0”这类问题如果你真的选择源码编译记得 GCC 强制要求 out-of-tree 构建也就是不能在源码目录下直接跑 configure必须建一个独立的 build 目录从那里指定源码路径。否则 configure 会直接拒绝执行这是 GCC 源码构建的硬性约定。4.2 “failed to verify gcc version”——工具链版本不匹配的真实案例“failed to verify gcc version. see log at /var/log/cuda-installer.log for det”这个报错核心就是安装器在装驱动时校验 gcc 版本失败了。这类硬件驱动安装脚本通常要求 gcc 版本落在某个区间内因为内核模块编译需要和当前内核头文件匹配的编译器环境。版本太高、太低都会拒绝继续安装。解决思路不是去改安装器而是去看日志里要求的具体版本范围然后安装对应版本的 gcc。如果你不想把系统默认 gcc 换掉也可以通过设置CC环境变量让安装脚本在编译时使用指定路径的旧版本编译器。这个方法不只适用于驱动安装很多 IDE 插件、SDK 工具链对编译器版本敏感时都可以这样处理。这里也顺带回答了“升级 gcc 的影响”这个问题编译一次程序可能看不出区别但那些依赖固定工具链版本的构建脚本、驱动模块和第三方库很容易因为你升级个编译器就被破坏。4.3 从旧工具链迁到新工具链最容易在 ABI 上翻车升级编译器后链接报一堆错很多时候不是代码写错了而是 C ABI 变化了。MinGW 工具链从 4.9.2 跳到新版本libstdc-6.dll的版本、STL 类的内存布局、异常处理模型都可能发生微妙变化Linux 下更明显旧程序链接新 so 时会出现GLIBCXX_3.4.x not found之类的符号版本错误。处理这类问题我个人的经验是要么源码全量重新编译所有依赖要么让构建环境固定在一个工具链版本上不要今天拿 4.9.2 编一个库、明天拿新版本编另一个库最后在链接阶段互相打架。Windows 侧尤其要注意不要混用 MSVC 编译的 .lib 和 MinGW 的 .a两个生态的 ABI 规则完全不同。5. 编译掉链子时我常用的四条排查经验5.1 从源码编译 GCC 慢问题出在 configure 和 make 策略看到“gcc make多久”这个问题很多人的第一步就跑偏了。GCC 默认是 3-stage bootstrap 构建就是自己编一遍再拿编出来的编译器编自己反复三次。这种模式在普通机器上跑动辄几个小时很正常。如果你只是想快速验证某个版本的编译行为等不起完整构建可以考虑在 configure 时加--disable-bootstrap只做单遍编译速度会快不少。另外--disable-multilib也很重要默认开启 multilib 时会额外编译多个目标变体又慢又容易失败。对于只想跑 64 位程序的人来说这两个参数能省下大量时间。5.2 不同 gcc 版本编出来的 so/dll 差异真的很大吗这个问题要分两层看。如果动态库导出的是一组 C 接口编译器版本差异对二进制兼容性的影响很小调用方只要按头文件声明调用即可一旦跨越动态库边界传递 C 对象比如std::string、std::vector编译器版本不同就容易出问题因为 STL 容器的内部实现可能已经变了。还有一个容易忽略的点是优化差异和默认指令集老版本 gcc 默认生成的代码可能相对保守新版本在优化策略上更激进最后出来的 so 体积、指令选择都不一样。所以正式发布前一定要锁定工具链版本不要把构建机上的 gcc 随手升来升去。5.3 breakpad 这类崩溃工具在 MinGW 下为什么容易出问题MinGW 下用 Google Breakpad 做崩溃收集不是不行但坑确实不少。Breakpad 的 Windows 端主要针对 MSVC/PDB 设计Linux 端用 DWARF 调试信息而 MinGW 的调试信息和 MSVC 的 PDB 完全不同。实际使用中新版dump_syms去解析 gcc 4.9.2 生成的 DWARF 信息经常出现符号文件生成失败或崩溃栈地址对不上的情况。我自己在 Windows 客户端项目里就吃过这个亏后来把dump_syms换成和工具链年代相近的版本才正常。如果你也走 MinGW breakpad 这条路记住一个原则工具链版本和崩溃收集工具要配套别一个升到 2024 年一个还停在上古时代。工具链老不是问题版本混乱才是问题。本文还有配套的精品资源点击获取
返回列表