:msinttypes補(bǔ)齊stdint.h,跨平臺編譯不再卡殼)
簡介在 Visual Studio 系列編譯器中缺少 C99 標(biāo)準(zhǔn)頭文件 stdint.h 與 inttypes.h 會觸發(fā) fatal error C1083: 無法打開包括文件根本原因是 VC 對 C99 標(biāo)準(zhǔn)支持不完整。這份修復(fù)包專門針對該報錯整理面向使用 VS2008 或其他老版本 VC 進(jìn)行 C/C 項(xiàng)目開發(fā)、需要引入標(biāo)準(zhǔn)整數(shù)類型定義的程序員。包內(nèi)共 3 個文件包括兩個頭文件與一個更新日志文本頭文件提供了 int8_t、uint32_t 等標(biāo)準(zhǔn)整數(shù)類型的聲明與平臺兼容定義日志則記錄了版本變更信息整體僅 17KB只包含修復(fù)所需的全部內(nèi)容足以直接解決該編譯錯誤且不需要額外依賴。資源已有 333 人學(xué)習(xí)下載體積小巧、針對性強(qiáng)適合在編譯報錯時快速定位解決對時間緊張的項(xiàng)目排障尤其友好。通過該包不僅能獲得可直接復(fù)用的缺失頭文件還能了解舊版 Windows SDK/VC 在 C99 支持上的限制與補(bǔ)全思路對維護(hù)老項(xiàng)目或跨平臺代碼有實(shí)用參考價值。1. 從C1083報錯說起這個資源到底是干什么的兩年前我接手一個跨平臺網(wǎng)絡(luò)庫的移植代碼在Linux上跑得好好的一挪到Windows上編譯控制臺直接蹦出一行刺眼的fatal error C1083: stdint.h。當(dāng)時第一反應(yīng)是“缺頭文件裝個SDK就行”折騰半天發(fā)現(xiàn)根本不是那回事——老版本的MSVC壓根沒實(shí)現(xiàn)C99的stdint.h而代碼里到處是uint32_t、int64_t。后來從某嵌入式論壇看到msinttypes-r26這個補(bǔ)丁包解壓后把路徑一配置編譯直接通過。這份資源本質(zhì)上是一個頭文件包專門幫老編譯器補(bǔ)上標(biāo)準(zhǔn)整數(shù)類型頭文件解決的就是“C1083且錯誤指向stdint.h”這類問題。適合所有在Windows上編譯開源代碼、做跨平臺移植、或者維護(hù)老舊C/C工程的開發(fā)者。2. stdint.h為何會在Windows上缺席C標(biāo)準(zhǔn)與編譯器的差距2.1 MSVC對C99支持的緩慢歷史C語言標(biāo)準(zhǔn)在1999年就提出了stdint.h和inttypes.h里面定義了帶固定寬度的整數(shù)類型比如int32_t、uint64_t還有格式化宏P(guān)RId64。這套頭文件在Linux、macOS、各種嵌入式編譯器里早就普及了但Windows平臺上的老牌C編譯器一直停留在C89/C90的舒適區(qū)對C99特性的支持能拖就拖。直到VS2010那一代才真正內(nèi)置了stdint.h之前的版本全都需要外部補(bǔ)丁。即使到了VS2010之后部分變體仍然在“有”和“沒有”之間反復(fù)橫跳讓人頭疼。為什么老牌編譯器這么抗拒C99常見理由是ABI兼容性和歷史代碼的穩(wěn)定性。Windows上有大量老程序依賴long類型32位、int類型32位的默認(rèn)行為如果按照C99的嚴(yán)格定義引入stdint.h可能會讓一些隱式類型轉(zhuǎn)換出現(xiàn)警告甚至錯誤。再加上Windows自己的SDK頭文件里也定義了一套類似的類型比如INT32、UINT64沖突風(fēng)險極高。所以廠商選擇了“不主動實(shí)現(xiàn)”讓開發(fā)者自己去想辦法。msinttypes就是這個場景下的產(chǎn)物。它最初是某個開源愛好者為了解決自家嵌入式項(xiàng)目的編譯問題而寫的后來不斷修復(fù)邊界情況r26版本就是其中的一個穩(wěn)定提交。它不編譯、不鏈接就是兩個頭文件加幾個輔助文件把C99標(biāo)準(zhǔn)里定義的類型和宏原樣翻譯成老編譯器能識別的typedef和#define。用上之后源碼里就不用再到處寫“#ifdef WIN32 typedef ...”這種丑陋的兼容層了。實(shí)際使用中你會發(fā)現(xiàn)很多開源項(xiàng)目在Windows上編譯失敗很大比例都是栽在stdint.h上。比如某個用C語言寫的物聯(lián)網(wǎng)協(xié)議棧內(nèi)部大量使用了uint16_t來解析報文在Linux上編譯順暢一拿到Windows上就報C1083新手往往會誤判成“SDK沒裝好”反復(fù)重裝環(huán)境最后發(fā)現(xiàn)只是缺一個頭文件。這種血淚經(jīng)驗(yàn)經(jīng)歷過一次就能記住。2.2 stdint.h在跨平臺代碼里的實(shí)際價值先來看為什么跨平臺代碼離不開stdint.h。C標(biāo)準(zhǔn)只保證了char、int、long這些基礎(chǔ)類型的最小范圍并沒有承諾它們在每個平臺上的位寬。在主流平臺上int通常是32位long在Linux 64位下是64位但在Windows 64位下仍然是32位。這意味著同樣的代碼在Linux上運(yùn)行正常換到Windows上就可能出現(xiàn)結(jié)構(gòu)體大小不對、序列化格式錯亂、位運(yùn)算結(jié)果異常等玄學(xué)問題。stdint.h提供了三組東西固定寬度整數(shù)類型intN_t/uintN_t、最小/最快寬度類型int_leastN_t、int_fastN_t、以及一些輔助宏INT_MAX、PRId64等。使用這些類型程序的行為就不會跟隨平臺自動漂移。比如定義一個網(wǎng)絡(luò)包頭結(jié)構(gòu)體用uint32_t字段替代unsigned int就能確保它在所有編譯環(huán)境下都是4字節(jié)而不是有的平臺4字節(jié)、有的平臺8字節(jié)。還有一點(diǎn)容易被忽略intptr_t和uintptr_t是用來保存指針值的整數(shù)類型。在32位程序里它是32位在64位程序里它是64位。如果你在做哈希或者把指針當(dāng)作數(shù)值運(yùn)算這個類型就是標(biāo)準(zhǔn)答案。在老編譯器上這些類型全都要靠自己typedefmsinttypes一次性幫你補(bǔ)齊。2.3 三種替代方案對比面對缺少stdint.h的編譯器你其實(shí)有三條路可以走。我根據(jù)自己的維護(hù)經(jīng)驗(yàn)列一張對比表方案優(yōu)點(diǎn)缺點(diǎn)適用場景自己寫一個自定義stdint.h完全可控?zé)o外部依賴容易寫錯類型寬度需要維護(hù)只有一個臨時工程代碼量極小用msinttypes補(bǔ)齊標(biāo)準(zhǔn)實(shí)現(xiàn)直接可用社區(qū)驗(yàn)證過需要額外引入文件不能自動更新絕大多數(shù)跨平臺移植和開源代碼編譯升級編譯器或換用其他工具鏈治本后續(xù)無需擔(dān)心C99特性可能牽動老項(xiàng)目ABI成本高新項(xiàng)目或允許改動編譯環(huán)境的場景我一般會推薦第二種。原因很簡單自己寫stdint.h你得考慮32位/64位、要不要兼容無符號、還要定義各種宏細(xì)節(jié)多到容易翻車。而且一旦有多個源碼目錄每個工程都塞一份自定義頭文件維護(hù)起來就是噩夢。msinttypes經(jīng)過多年修補(bǔ)對老編譯器的兼容性做得比較完善r26這個版本我用了幾年沒出過岔子。選擇它還有一個隱性好處源碼不用再寫一堆“#ifdef _MSC_VER”繞道代碼了。整個工程在一個統(tǒng)一的頭文件視角下編譯后續(xù)如果想切換到新編譯器直接把msinttypes從包含路徑里移除就行代碼不需要動。這種“后悔藥”的效果經(jīng)歷過老工程現(xiàn)代化改造的人都懂。再說說和標(biāo)準(zhǔn)C語言的對應(yīng)關(guān)系。stdint.h還定義了一組寬度宏比如INT8_C、INT16_MAX這些在初始化常量時非常有用。例如你想定義一個恰好等于INT32_MAX的宏常量直接寫#define LIMIT INT32_C(2147483647)比起手動算0x7FFFFFFF更清晰。msinttypes里也完整實(shí)現(xiàn)了這些宏而且針對老編譯器可能不支持的部分做了變通。如果你之前只關(guān)注了類型定義忽略了這些宏那在寫可移植代碼時可能會踩坑。另外inttypes.h里還有strtoimax、strtoumax等函數(shù)聲明以及printf格式宏P(guān)RId32、PRIu64。在Windows上用printf打印長整型常用的做法是%I64d但這些是非標(biāo)準(zhǔn)擴(kuò)展。用了inttypes.h就可以寫PRId64代碼在Linux和Windows上都能編譯不用再為格式化字符串傷腦筋。這個細(xì)節(jié)在移植串口調(diào)試代碼時尤其常見。3. msinttypes-r26上手三步配好編譯器包含路徑3.1 解壓看結(jié)構(gòu)里面到底有什么下載下來的壓縮包體積很小解壓后一多半是說明文檔和測試代碼真正核心的文件只有兩個stdint.h和inttypes.h。我以r26版本為例列出常用的文件清單文件作用stdint.h定義全部整數(shù)類型和大小限制宏inttypes.h定義格式化宏和轉(zhuǎn)換函數(shù)聲明LICENSEBSD風(fēng)格的開源許可README使用說明和版本歷史這里的文件結(jié)構(gòu)很干凈不需要額外安裝腳本。你需要做的就是把整個解壓目錄加入編譯器的頭文件搜索路徑。一個常見的做法是把該目錄復(fù)制到工程目錄下的third_party/msinttypes或者放到一個公共的依賴庫目錄里比如D:\dev\include。復(fù)制好之后建議用文本編輯器打開stdint.h確認(rèn)文件開頭包含“#ifdef _MSC_VER”之類的條件編譯代碼。msinttypes在內(nèi)部會根據(jù)_MSC_VER宏判斷編譯器版本再決定是否啟用typedef。比如在VC6這樣的老古董上它走一套完全不同的定義。如果你用較新的編譯器它也會自覺避免和系統(tǒng)自帶的stdint.h沖突。3.2 通過IDE屬性頁配置包含路徑最常見的使用方式是在Windows IDE里打開項(xiàng)目屬性進(jìn)行配置。操作路徑是項(xiàng)目右鍵-屬性-C/C-常規(guī)-附加包含目錄。點(diǎn)開編輯框手動輸入msinttypes所在的目錄路徑或者點(diǎn)旁邊的下拉按鈕選“編輯”再點(diǎn)文件夾圖標(biāo)瀏覽目錄。配置完成后記得先把語言標(biāo)準(zhǔn)設(shè)置成“未設(shè)置”或C89兼容模式否則某些IDE可能強(qiáng)行走新的頭文件搜索邏輯。這里有個容易忽略的點(diǎn)附加包含目錄的作用范圍是當(dāng)前項(xiàng)目配置不是全局的。如果你同時維護(hù)Debug和Release兩個配置并且都在用老編譯器兩邊都得添加一次。我見過有同事只改了DebugRelease還在報C1083糾結(jié)了半天最后發(fā)現(xiàn)是這條路沒走全。建議在屬性頁頂部的“配置”下拉框選擇“所有配置”一勞永逸。配置完成后清理并重新編譯整個工程。如果原來的報錯是“未找到stdint.h”此時應(yīng)該消失。如果還報錯看看錯誤消息里的文件路徑是否指向了別的位置的stdint.h。這里容易中招的是項(xiàng)目里某個第三方庫也帶了一個stdint.h并且它的目錄在附加包含目錄列表里排在更前面那編譯器找到的就不是msinttypes的版本。關(guān)于這個路徑配置還有一個小技巧把msinttypes目錄放在附加包含目錄列表的最前面。這樣能保證優(yōu)先級最高避免和其他庫頭文件混戰(zhàn)。列表順序在屬性頁里可以直接上下移動值得養(yǎng)成習(xí)慣。3.3 通過命令行cl.exe直接配置不進(jìn)IDE直接用命令行編譯的情況也非常多。比如用cl.exe配合Makefile、批處理腳本或CMake。此時只需要多傳一個/I參數(shù)告訴編譯器在哪里找頭文件。cl /I D:\dev\include\msinttypes /Tc test_stdint.c這里的 /I 是附加包含目錄的短參數(shù)后緊跟目錄路徑。如果路徑有空格必須用雙引號包起來。/Tc 表示把后續(xù)文件當(dāng)作C語言源碼編譯如果想編譯C代碼改成 /Tp 或直接給.cpp擴(kuò)展名。執(zhí)行后如果沒報錯會在同目錄下生成test_stdint.obj文件。有些項(xiàng)目用的是nmakeWindows下的make工具那就在Makefile里設(shè)置CFLAGS變量加上該路徑。示例CFLAGS /nologo /ID:\dev\include\msinttypes /W3這里 /W3 是警告級別按需調(diào)整。在Makefile里寫路徑時要注意反斜杠和引號的轉(zhuǎn)義習(xí)慣nmake對這些相對寬松但遇到空格時一定要引號閉合。如果你是在PowerShell或CMD里臨時測試不想加進(jìn)工程配置也可以直接用環(huán)境變量INCLUDE來臨時注入。命令行設(shè)置方式如下set INCLUDE%INCLUDE%;D:\dev\include\msinttypes這個命令的作用范圍是當(dāng)前控制臺窗口不會改動系統(tǒng)級配置適合快速驗(yàn)證。Windows的編譯器在查找頭文件時會先查命令行參數(shù)指定的路徑再查環(huán)境變量INCLUDE所指的路徑。所以如果你的INCLUDE已經(jīng)包含了一個有stdint.h的地方可能不生效這時需要檢查順序。3.4 集成到CMake工程用CMake組織的跨平臺工程配置msinttypes比手動改項(xiàng)目屬性要優(yōu)雅得多。你不需要碰IDE的界面只要在CMakeLists.txt里加上一行include_directoriesinclude_directories(${PROJECT_SOURCE_DIR}/third_party/msinttypes)這行代碼把msinttypes目錄加入所有編譯目標(biāo)的頭文件搜索路徑。需要確保該目錄實(shí)際存在于源碼樹中并且路徑名稱與目錄真實(shí)名稱一致。如果你的構(gòu)建系統(tǒng)是從網(wǎng)上下載依賴還可以考慮用FetchContent模塊在配置階段自動下載但這里不建議因?yàn)轭^文件本身不依賴網(wǎng)絡(luò)直接隨源碼分發(fā)更省事。CMake中還有一個細(xì)節(jié)MSVC的編譯器在編譯C文件時會自動開啟C89模式編譯C時不會受到影響。如果你的CMake項(xiàng)目里同時有C和C文件并且在C代碼中也用了stdint.hmsinttypes是可以起作用的因?yàn)镃里同樣會include頭文件。唯一要注意的是如果你設(shè)置了標(biāo)準(zhǔn)庫強(qiáng)制使用Windows DLL運(yùn)行時即/MD或/MT開關(guān)那msinttypes和這些宏是獨(dú)立的互不影響。如果項(xiàng)目還使用了預(yù)編譯頭PCH記得在預(yù)編譯頭文件如stdafx.h里#include stdint.h否則每個.cpp文件單獨(dú)include也可以但會拖慢編譯。工程里如果已有預(yù)編譯頭而你在某個.cpp里直接#include stdint.h某些IDE會提示需在預(yù)編譯頭之后include此時調(diào)整一下順序即可。4. 驗(yàn)證stdint.h生效從編譯通過到類型寬度一致4.1 一個專門驗(yàn)證固定寬度類型的示例代碼配置完成后直接拿一個大項(xiàng)目編譯風(fēng)險太大我習(xí)慣先寫一個最小的驗(yàn)證文件專門檢驗(yàn)頭文件有沒有生效、類型寬度是否符合預(yù)期。#include stdio.h #include stdint.h #include inttypes.h int main(void) { printf(sizeof(int32_t) %zu\n, sizeof(int32_t)); printf(sizeof(uint64_t) %zu\n, sizeof(uint64_t)); printf(sizeof(intptr_t) %zu\n, sizeof(intptr_t)); int32_t a INT32_C(100); uint64_t b UINT64_C(0x1122334455667788); printf(a % PRId32 , b % PRIx64 \n, a, b); return 0; }這段代碼里用到了三組東西固定寬度類型sizeof檢查、INT32_C宏常量、以及PRId32/PRIx64格式化宏。如果頭文件路徑正確編譯后會輸出類似這樣的內(nèi)容sizeof(int32_t) 4 sizeof(uint64_t) 8 sizeof(intptr_t) 4 a 100, b 1122334455667788注意第三行如果編譯出來的程序是32位的intptr_t就是4字節(jié)如果是64位會是8字節(jié)。這行輸出能幫你確認(rèn)編譯目標(biāo)環(huán)境。格式化宏P(guān)RId32在Windows的老編譯器上會展開成“d”PRIx64會展開成“I64x”這兩個宏正是inttypes.h里的關(guān)鍵內(nèi)容。4.2 通過編譯日志確認(rèn)頭文件實(shí)際路徑有時候編譯通過了但心里還是不踏實(shí)想知道編譯器到底用的是哪個目錄下的stdint.h。這時候可以采用預(yù)處理指令強(qiáng)制輸出路徑信息。最簡單的方法是讓編譯器生成預(yù)處理文件。cl /I D:\dev\include\msinttypes /E test_stdint.c preprocessed.txt這里的 /E 是預(yù)處理后輸出到標(biāo)準(zhǔn)輸出重定向到文本文件。打開preprocessed.txt搜索“stdint.h”就會看到完整的頭文件路徑。例如#line 1 D:\\dev\\include\\msinttypes\\stdint.h這一行說明編譯器確實(shí)使用了你指定的msinttypes版頭文件。如果你看到路徑是某個Windows SDK目錄或者項(xiàng)目目錄下的其他頭文件那就說明搜索順序有問題需要回到第3章調(diào)整路徑順序。除了 /E還可以在代碼里用#pragma message直接把頭文件路徑打印到編譯輸出窗口。這個方法適合在IDE里使用因?yàn)榫幾g器輸出會直接顯示在“輸出”面板中不用生成中間文件。#ifdef _MSC_VER #pragma message(using msinttypes stdint.h) #endif把這段放在一個共同的私有頭文件里或者直接放在測試代碼頂部編譯時就能看到提示。注意#pragma message是編譯器擴(kuò)展不是標(biāo)準(zhǔn)C語言所以外面包了一層條件編譯避免其他編譯器報錯。4.3 在Makefile和批處理腳本里復(fù)用的細(xì)節(jié)如果你的工程是用Makefile組織的或者你更喜歡批處理腳本一鍵編譯驗(yàn)證邏輯也可以嵌進(jìn)腳本里。我自己的習(xí)慣是寫一個check_stdint.bat腳本在配置階段自動運(yùn)行上面的預(yù)處理命令并檢查輸出里是否出現(xiàn)了預(yù)期的路徑標(biāo)記。echo off cl /I D:\dev\include\msinttypes /E test_stdint.c tmp_pre.txt findstr /C:msinttypes\stdint.h tmp_pre.txt nul if errorlevel 1 ( echo [FAIL] stdint.h not from msinttypes exit /b 1 ) echo [OK] stdint.h found from msinttypes del tmp_pre.txt這個腳本的邏輯很簡單用findstr檢查預(yù)處理輸出里是否包含“msinttypes\stdint.h”字符串找到了就返回成功否則輸出失敗并退出。批處理通過errorlevel判斷可以在構(gòu)建流水線里集成。對于使用GCC或Clang但也在Windows上交叉編譯的場景同樣可以用#error預(yù)處理指令做更硬的驗(yàn)證。例如在測試代碼里加入#ifndef INT32_MAX #error stdint.h does not define INT32_MAX #endif當(dāng)INT32_MAX沒有被定義時編譯直接終止并顯示錯誤信息。這種編譯期驗(yàn)證方式比人工看日志更快適合自動化集成。注意這里的宏定義名必須和實(shí)際頭文件一致msinttypes里確實(shí)定義了這些宏。5. 避坑指南C1083之外的五種常見翻車現(xiàn)場這一章是重頭戲我根據(jù)自己踩過的坑和幫同事排查的經(jīng)驗(yàn)整理了五條最容易遇到的錯誤。5.1 坑1和其他庫自帶的stdint.h打架現(xiàn)象編譯時報一堆標(biāo)準(zhǔn)庫頭文件重復(fù)定義錯誤錯誤列表中全是“redefinition of typedef ‘int32_t’”、“missing type specifier”等字樣錯誤數(shù)量能達(dá)到幾十條。原因項(xiàng)目里某個第三方庫自己攜帶了一個stdint.h或者系統(tǒng)SDK的新版本已經(jīng)包含了該頭文件。由于多個目錄都存在stdint.h編譯器按照搜索順序找了一個不期望的版本兩個標(biāo)準(zhǔn)庫的內(nèi)容彼此沖突。解決找到工程中所有名為stdint.h的文件確認(rèn)msinttypes的目錄是否在搜索列表最前面??梢栽趯傩皂撝姓{(diào)整順序或者通過在C/C命令行中加入?yún)?shù)指定強(qiáng)制使用msinttypes的路徑。如果第三方庫的頭文件必須優(yōu)先那就只能手動把庫自帶的stdint.h改名并把庫源碼里對應(yīng)的#include聲明一并修改。更穩(wěn)妥的辦法是用編譯開關(guān)查看預(yù)處理搜索路徑先確認(rèn)到底誰被優(yōu)先找到了。5.2 坑2類型寬度和預(yù)期不一致現(xiàn)象代碼編譯通過但運(yùn)行時的結(jié)構(gòu)體大小、序列化字節(jié)數(shù)和Linux同事不一樣調(diào)試半天發(fā)現(xiàn)uint32_t變成了8字節(jié)。原因某些老的msinttypes版本在特定編譯器上沒有正確區(qū)分指針類型的寬度導(dǎo)致定義錯誤。也可能是因?yàn)榫幾g器選項(xiàng)里啟用了/arch:IA32或強(qiáng)制4字節(jié)對齊等設(shè)置讓類型對齊發(fā)生變化。解決先用第4章的驗(yàn)證程序打印sizeof確認(rèn)寬度。如果確實(shí)是頭文件定義問題檢查是否使用了正確版本r26對64位和32位分別做了分支處理。再檢查編譯選項(xiàng)里是否定義了_WIN32或_WIN64宏msinttypes內(nèi)部依賴這些宏做位寬分支。在編譯命令行中手動添加/D_WIN64可以強(qiáng)制走64位路徑。我遇到過最詭異的一次是工程屬性里的“平臺工具集”被誤設(shè)成了32位導(dǎo)致所有源文件都被當(dāng)作32位編譯加上/D_WIN64之后反而報宏重定義最后發(fā)現(xiàn)是工具集版本不匹配。5.3 坑3C代碼能編譯但C代碼報錯現(xiàn)象同一個工程.c文件編譯正常.cpp文件卻報“stdint.h: No such file or directory”或語法錯誤錯誤行指向頭文件內(nèi)部的某個宏。原因老編譯器在編譯C時對C99頭文件的支持路徑不同。msinttypes的頭文件內(nèi)部包含了一些條件編譯比如“#ifndef __STDC_LIMIT_MACROS #error ...”這個錯誤只在C中觸發(fā)因?yàn)镃標(biāo)準(zhǔn)規(guī)定這些宏默認(rèn)不暴露。解決在C源文件包含stdint.h之前先定義必要的宏。一般處理是在工程屬性中加上預(yù)處理器定義/D__STDC_LIMIT_MACROS /D__STDC_CONSTANT_MACROS這兩個宏是C標(biāo)準(zhǔn)里用來把C99的宏暴露給C的開關(guān)。msinttypes也遵守這個約定。加上之后C代碼中就能正常使用INT32_MAX和UINT64_C等宏了。如果仍報錯檢查頭文件內(nèi)部是否還有__STDC_FORMAT_MACROS需要定義用于獲取printf格式化宏。這個宏在C里同樣默認(rèn)關(guān)閉忘記加的話PRId64會直接缺定義。5.4 坑4路徑配置正確但還是提示找不到stdint.h現(xiàn)象項(xiàng)目屬性里明明加了附加包含目錄編譯時依然報C1083。原因三種典型原因。一是配置的是“調(diào)試信息”而不是“包含路徑”二是配置的路徑下沒有對應(yīng)的文件比如解壓后把文件夾嵌套了一層三是IDE緩存問題清理不徹底。解決先用系統(tǒng)文件搜索確認(rèn)完整路徑比如D:\dev\include\msinttypes\stdint.h確實(shí)存在。然后檢查屬性頁的“附加包含目錄”填寫是否就是該目錄不要把“include\msinttypes”多寫或少寫。還要關(guān)注配置類型如果是同一個工程包含靜態(tài)庫和DLL項(xiàng)目每個子項(xiàng)目都要各自配置。最后嘗試關(guān)閉IDE重開或者清理中間文件。我甚至見過有人把路徑寫成了“D:\dev\include\stdint.h”少了msinttypes這一層自然找不到。這個坑看似低級但忙亂的時候很容易犯。5.5 坑5inttypes.h中的格式化宏導(dǎo)致printf警告現(xiàn)象用PRId64打印int64_t變量時編譯器報格式字符串參數(shù)類型不匹配的警告。警告級別開得越高報得越多甚至直接導(dǎo)致編譯失敗。原因老編譯器對printf系列的格式化檢查識別不了自定義宏或者宏展開后是“I64d”這種非標(biāo)準(zhǔn)格式編譯器的警告級別過高時會產(chǎn)生警告。這個警告不影響生成但會讓追求0警告的工程無法通過。解決把警告級別從/Wall降到/W3或者在包含inttypes.h之前定義__STDC_FORMAT_MACROS宏。在老編譯器上也可以直接使用系統(tǒng)擴(kuò)展格式符號比如printf(value%I64d\n, (long long)value);不過這樣就犧牲了可移植性。我的做法是在工程級統(tǒng)一加一個頭文件提前定義好宏并設(shè)置#pragma warning抑制掉特定的警告編號。記住一個原則寧可多花十分鐘把所有格式化宏檢查一遍也別等到集成測試時才發(fā)現(xiàn)日志亂碼。6. 進(jìn)階驗(yàn)證用靜態(tài)斷言確保類型寬度不再漂移6.1 在編譯期鎖死寬度當(dāng)你給老工程配上msinttypes之后編譯通過只是第一步我更建議在代碼里加上編譯期靜態(tài)斷言把類型寬度徹底鎖死。C語言中沒有標(biāo)準(zhǔn)靜態(tài)斷言可以用typedef數(shù)組技巧typedef char assert_int32_size[ (sizeof(int32_t) 4) ? 1 : -1 ];如果int32_t不是4字節(jié)這個typedef會定義一個長度為-1的數(shù)組導(dǎo)致編譯錯誤。C99后的編譯器可能支持_Static_assert但老編譯器不支持所以這個技巧更穩(wěn)。把它放在一個公共頭文件里所有模塊都會檢查到。6.2 用預(yù)處理宏輸出當(dāng)前環(huán)境在排錯時經(jīng)常需要確認(rèn)當(dāng)前編譯器模式。以下代碼可以把關(guān)鍵宏打印在編譯輸出中#ifdef _MSC_VER #pragma message (MSVC version: STRINGIFY(_MSC_VER)) #endif需要配合一個字符串化宏#define STRINGIFY(x) #x這條pragma只在MSVC下生效GCC對應(yīng)的是#warning但也不妨礙體驗(yàn)。加上之后每次編譯都能看到版本號省去猜謎時間。6.3 我的自查流程從那以后我每次把一個新工程移植到Windows都會強(qiáng)制走一遍這個流程先跑一個最小的stdint驗(yàn)證程序確認(rèn)類型寬度再檢查INT32_MAX、PRId64這些宏是否可用最后用上面的靜態(tài)斷言把類型鎖死在公共頭文件里。等這一套做完我才敢繼續(xù)編譯主體代碼。經(jīng)歷過因?yàn)橐粋€類型寬度不一致導(dǎo)致整個協(xié)議棧解析錯亂的事故就再也不想省這一步了。希望這些細(xì)節(jié)能幫到你讓你在Windows上遇到stdint.h問題時少走我當(dāng)初的彎路。本文還有配套的精品資源點(diǎn)擊獲取