ARTICLE DETAIL

资讯详情

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

msinttypes-r26.rar:老版Visual Studio补全C99整数类型头文件的实用指南

msinttypes-r26.rar:老版Visual Studio补全C99整数类型头文件的实用指南 简介在 Visual C 系列编译环境中依赖 C99 标准新增头文件的代码若遇到旧版编译器常触发 fatal error C1083 错误提示无法打开包括文件。此类问题多见于 VS2008 及更早版本原因是开发工具未内置相应头文件开源库或新代码无法顺利编译。此补丁包面向需要在老旧 VC 环境迁移或构建 C/C 项目的开发者压缩包内共三个文件包括两个 h 头文件和一个 txt 更新说明体积仅十七 KB。其中头文件分别提供整数类型定义与格式宏支持更新说明则记录版本变化。使用时将两个头文件复制到编译器默认的 include 目录例如 VS2008 安装路径下的 VC\include 文件夹重新编译即可消除中断点全程无需改动源代码。目前已有三百三十人学习下载对受困于编译环境缺口的程序员这是一份高效直接的排错工具。 相信不少人搜到msinttypes-r26.rar这个文件名第一反应都是这年头怎么还有人在传 rar 包但如果你正对着 Visual Studio 2010 甚至 2008 的编译输出满眼都是“无法打开包括文件stdint.h”这类报错那你大概就理解这个看似老旧的压缩包为什么还能在工程圈子里一直流传了。msinttypes 是一个专门给老版本 MSVC 提供 C99 固定宽度整数类型头文件的开源小项目r26是它的第 26 个 release 版本也是在各种下载站里最常见的一个版本号。这个包解决的事情很纯粹让 Visual Studio 在没有原生stdint.h的情况下也能编译 Linux/Unix 风格的 C/C 代码。这篇文章我把这些年实际用它、折腾它、最后把它留在项目仓库里的经验全部梳理了一遍从原理到操作再到坑都写在下面。1. 一个 rar 文件解决的时代难题你为什么需要 msinttypes1.1 msinttypes-r26 到底是个什么东西先把定位说清楚msinttypes-r26.rar不是一个软件也不是一个 SDK解压之后里面其实只有两个文本文件stdint.h和inttypes.h。就是这样两个头文件没有.lib没有.dll更没有安装向导。你要做的就是把这两个文件放到编译器能够搜索到的 include 目录里然后让代码里原有的#include stdint.h和#include inttypes.h正常编译过去。在我刚接触这个包的时候甚至怀疑过这玩意儿是不是来搞笑的可我把它放到项目里重新编译那批从 Linux 移植过来的代码报错立刻少了一大半那一瞬间我才知道这种不起眼的文件才是真正解决问题的东西。因为 Windows 上微软自家的 C 编译器在相当长一段时间里并不完整支持 C99 标准而 C99 里规定的那些整数类型定义和格式化宏对跨平台代码来说又是刚需。msinttypes 就是专门来补这个缺口的。1.2 为什么缺一个 stdint.h 会搞得这么狼狈很多人现在用 VS2015、VS2019、VS2022可能已经感受不到这种痛了。但在当年一个从 Linux 迁移到 Windows 的 C 项目最常见的编译失败就是下面这种c:\project\src\common\types.h(42): fatal error C1083: Cannot open include file: stdint.h: No such file or directory接着就是一堆uint32_t、int64_t、INT32_MAX这些标识符的未定义错误一个文件报错整个工程看起来就没法编译了。原因很简单MSVC 在实现 C99 标准时并不完整VS2010 及更早版本里压根没有提供一个标准的stdint.h。而跨平台项目为了可移植性几乎都会用固定宽度整数类型比如uint32_t可以明确表示无符号 32 位整数int64_t则代表有符号 64 位整数。换到 Windows 下这些类型缺失你总不能把所有代码都改一遍。也有人说“没有头文件了我自己 typedef 不就行了”。可以但如果每个项目都自己定义一套不同项目的int64_t可能有的用long long有的用__int64有的用long时间一长格式化、重载、跨语言调用就会出各种混乱。msinttypes 的价值就是用一套统一、可考证的typedef定义把大家从这种混乱里解放出来。2. 拆开 r26stdint.h 与 inttypes.h 里面到底做了什么2.1 stdint.h 的 typedef 策略从原理上看stdint.h做的工作并不复杂本质上就是对各类整数类型做一套别名定义。以 r26 版本的思路为例它针对 MSVC 的编译器特点做了一些特殊处理大致类似这样typedef signed char int8_t; typedef short int16_t; typedef int int32_t; typedef __int64 int64_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef unsigned __int64 uint64_t;这段代码不是我从 r26 官方文件里原样抄下来的但代表的逻辑基本一致。这里最容易让人疑惑的是为什么 64 位整数要用__int64而不是long long。因为在老版本 MSVC 里long long要么不被支持要么在语言层面支持得不够完整而__int64是微软编译器自己很早就有的一种类型扩展用起来最稳妥。除固定宽度整数之外stdint.h还会定义intptr_t和uintptr_t。这两个类型是用来存放指针的整数类型也就是说在 32 位编译环境下应该具备 32 位宽度在 64 位环境下应该具备 64 位宽度。msinttypes 在编写时会根据_WIN64这样的宏来做条件区分确保代码在两种模式下都能编译。2.2 inttypes.h 里的格式化宏和它的兼容逻辑很多人以为有了stdint.h就万事大吉了实际不是。C 代码里除了类型声明还有大量的printf格式化输出。你用int64_t声明了一个变量然后想把它打印出来总得知道格式串该写什么吧。在 GCC 环境下64 位整数通常用%lld或者%lu来打印但在老版 MSVC 的 CRT 里打印 64 位整数用的却是%I64d、%I64u。如果代码里直接写死格式符换一个编译器就得改一遍那还谈什么跨平台。inttypes.h就是为了解决这个问题的。它定义了一组格式化宏例如#define PRId32 d #define PRIu32 u #define PRId64 I64d #define PRIu64 I64u业务代码里不直接写%I64d而是写PRId64比如printf(value % PRId64 \n, some_int64_value);这样在 GCC 环境下PRId64会被展开成lld在老版 MSVC 环境下它会自动展开成I64d。代码不用改编译器自己就把平台差异消化掉了。这里有一个在 C 项目里特别容易踩的坑这些宏都是全局宏不是命名空间里的符号。你在代码里不能用std::PRId64这种写法应该直接写PRId64。如果你觉得这个宏名太丑想包一层也要注意宏替换时机别在封装的时候把它当函数参数给传丢了。3. 实操集成下载解压、include 目录、冲突排查3.1 解压之后到底该把文件放哪先说我的推荐做法不要把stdint.h和inttypes.h直接解压到 Windows SDK 的 Include 目录里。虽然这样也能生效但后患无穷。万一哪天项目升级到了新版编译器系统自带的stdint.h和 msinttypes 里的同名头文件开始冲突你要满系统去删一个不起眼的文本文件那体验是真的酸爽。更好的做法是在项目仓库里单独建一个目录比如third_party/msinttypes/stdint.h third_party/msinttypes/inttypes.h然后把third_party/msinttypes这个路径添加到项目的附加包含目录中。VS 的老版工程在“项目属性 - 配置属性 - C/C - 常规 - 附加包含目录”里加一行相对路径就行。这样整个项目随仓库走换新机器、换同事电脑只需要拉代码不需要重新配环境。3.2 附加包含目录的顺序会影响编译器先找谁这里就引出一个绝大多数刚接触 msinttypes 的人没想过的问题附加包含目录的搜索顺序。Visual Studio 的头文件搜索顺序大致是源文件所在目录、附加包含目录、系统环境变量里的 INCLUDE 目录、编译器默认目录。如果你的工程把third_party/msinttypes放在附加包含目录里而且这个目录排在靠前的位置那么编译器遇到#include stdint.h时会优先把 msinttypes 的这个文件拿过来。在 VS2010 里这样做问题不大因为系统本来就没有标准stdint.h。但到了 VS2013 之后微软开始提供官方的stdint.h如果你仍然保留了 msinttypes 的旧文件它可能会把官方头文件“截胡”导致某些新类型定义缺失或者出现重复定义错误。我见过一个比较典型的情况工程同时用了 Windows SDK 里的stdint.h功能又引了一份 msinttypes最后编译时爆出一堆C2011: int32_t : typedef redefinition。这种问题往往不是代码逻辑错误而是同一个工程里存在两份同名头文件的冲突。所以排查这类问题时第一步应该是用预处理输出还原“真凶”。在 VS 里可以通过“C/C - 预处理器 - 预处理到文件”或者在命令行下执行cl /E /P /Ithird_party/msinttypes your_source.c preprocessed.txt然后去预处理后的文件里查看#line标记它能准确告诉我们每个stdint.h内容到底来自哪个物理路径。只要看清这一条大部分包含冲突都好办。3.3 检查你的引用方式是否统一还有一个特别常见的低级问题项目里有的人写#include stdint.h有的人写#include stdint.h。两种写法会导致编译器以不同的方式搜索头文件一旦目录里面存在同名文件结果就可能不一样。建议在老项目里统一改成尖括号形式#include stdint.h因为尖括号的搜索路径更可控便于在附加包含目录里进行优先级管理。当然如果你是想优先使用项目目录下某个自定义版本那#include stdint.h也有它的作用关键是团队里面要有统一约定别混着写。4. 用下来发现的坑x64、C、权限问题都要留意4.1 intptr_t 在 32 位和 64 位下的差异这类头文件平时不用的时候感觉什么都没干编译的时候动不动就给你整点活儿其中最容易翻车的就是intptr_t和uintptr_t。从语义上说intptr_t设计出来就是为了把指针强转成整数。既然指针在 64 位编译下是 64 位宽度那intptr_t也必须是 64 位的。msinttypes 在底层会通过_WIN64宏来判断当前编译目标32 位编译时把intptr_t定义为int或__int3264 位编译时把它定义为__int64。这个机制本身没什么问题问题出在有些人会在自己的代码里额外写一份“兼容层”头文件里面已经有类似下面的定义#if defined(_WIN64) typedef __int64 intptr_t; #else typedef int intptr_t; #endif一旦这样的定义和 msinttypes 里的定义同时生效编译器就会老老实实报一个 typedef 重定义错误。这种事我见过不止一次。所以使用 msinttypes 之前一定要先检查项目里有没有“自研”的stdint.h或者已经散落的各种typedef __int64 int64_t有的话先删掉让标准头文件统一接管。如果项目是打 CI 的强烈建议 32 位和 64 位两个目标都编译一遍。很多问题在 x64 下不会冒出来一切到 Win32 就原形毕露。4.2 在 C 里用这些类型时的注意点msinttypes 的头文件本身可以被 C 和 C 共用这一点不用怀疑。它定义的类型和宏默认都在全局命名空间里所以 C 代码里直接写uint32_t是没问题的。但有一个习惯要纠正不要在using namespace std;之后默认uint32_t就一定在std命名空间里。C11 标准虽然把cstdint里的类型放进了std命名空间但在老编译器项目里你不一定用的是标准库的cstdint很可能还是跑到了 msinttypes 的全局定义上。所以最稳妥的写法就是直接用全局用法uint32_t而不要写std::uint32_t这种依赖具体环境的东西。另外关于 C 中格式化宏的用法也要注意PRId64是宏不是变量也不是函数。有人会写成printf(% PRIu64 \n, x)也有人会试图把它包进一个日志封装里void log_value(int64_t v) { printf(value% PRId64 \n, v); }这样写没问题。如果没有把宏直接放在字符串字面量里正常替换掉编译器会在检查格式串时误判然后报一堆 warning不用慌看下预处理结果就能明白是宏替换的问题。4.3 文件权限、杀软和其他环境折腾最后说一个跟技术关系不大但实际体验非常真实的地方r26 这个 rar 下载下来之后解压路径不要太随意。把stdint.h解压到 Program Files 系统目录下存在两个问题一是后续编译时普通用户权限可能没有写入会导致奇怪问题二是杀毒软件会密切盯着这些系统目录里的文件改动一变就被拦截。老项目维护本身就是个辛苦活没必要在这种地方给自己加戏。所以还是那句解压到一个干净的第三方库目录里路径不要带空格也不要有中文。老 C 工程最常见的“莫名其妙编译不过”很多就出在路径这种东西上。5. 新版编译器和替代方案什么时候不用再依赖它5.1 不同 VS 版本下的兼容底线我用这个头文件摸过几个 VS 版本简单整理了一个兼容性参考Visual Studio 版本是否自带 stdint.h是否需要 msinttypes备注VS2005 / VS2008否需要跨平台 C 项目基本都离不开它VS2010否需要老代码移植时最常用的搭配VS2012早期版本不完整建议保留可用 msinttypes 做统一覆盖VS2013是但细节有限视情况而定注意冲突优先用系统头文件VS2015 及以上是UCRT 提供不需要新项目直接使用标准头文件即可如果你现在用的是 VS2015 之后的版本那完全没有必要再去集成 msinttypes 了。Windows SDK 里的 UCRT 已经提供了完整的stdint.h和inttypes.h而且和微软自家的 CRT 配合得更好。但如果你还在维护一个 VS2010 时代的老工程那 msinttypes-r26 基本就是最有性价比的选择。别折腾什么自研头文件也别想着让整个工程迁移到 C11 再解决改变越少、风险越小。5.2 迁移到现代环境时怎么摘掉 msinttypes假设有一天你想把老项目升级到 VS2015 以上但又怕运行行为发生变化那摘除 msinttypes 的时候可以按步骤来先把附加包含目录里的msinttypes路径移除重新编译看哪里引用了旧头文件搜索代码里所有#include stdint.h改成#include stdint.h检查有没有自定义的int64_t、uintptr_t之类的 typedef有就清掉编译后重点跑一遍所有用到PRId64、PRIu64的日志模块看看格式输出是否正常。最后一步格外重要。因为新版 VS 在 64 位整型格式化方面基本遵循 C 标准如果你之前的代码为了迁就%I64d写过一些字符串拼接、日志解析逻辑换到系统头文件之后这些行为不会自动跟着变需要手工回归验证。5.3 我为什么不随便删掉它现在的编译环境确实不太需要 msinttypes 了但我在几个老项目里还是保留着third_party/msinttypes这个目录。原因很简单项目里有一些老旧的第三方库会在公共头文件里#include stdint.h而项目本身还挂着 VS2010 编译节点删掉这个目录就意味着所有老编译节点全部要改配置。对这种还能正常跑的东西我不想因为“新环境不需要”这几个字去折腾整个供应链。同时很多下载站里的msinttypes-r26.rar是没有严格哈希校验的因为项目本身已经停止更新很久网上流传的压缩包质量参差不齐。我的建议是拿到手先查毒解压后确认只有stdint.h和inttypes.h两个文件然后提交到自己的代码仓库里自己给自己留一份可追溯的存档。如果哪天某个下载链接失效了你本地的版本库就是最靠谱的备份。最后再分享一个小技巧。因为msinttypes-r26.rar本身不过几十 KB收集到一个公共仓库里不占空间我习惯在工程根目录里留一个像是这样的引用说明third_party/msinttypes/ README.txt stdint.h inttypes.hREADME.txt里只写一行字给 VS2010 老编译节点使用的 C99 整数类型头文件来源为 msinttypes r26。这样以后任何人接手这个仓库看到这个目录都不会一头雾水也不会因为好奇去乱改它。本文还有配套的精品资源点击获取
返回列表