)
簡介這份壓縮包面向?qū)W習(xí) C 圖形繪制的初學(xué)者、課程實驗者以及需要補全 BGI 圖形庫環(huán)境的開發(fā)者提供 graphics.h 頭文件與 graphics.lib 庫文件的配套組合用于解決編譯鏈接階段缺少圖形接口聲明和預(yù)編譯實現(xiàn)的問題并適配常見的 Windows 編譯環(huán)境。包內(nèi)共 2 個文件分別為 1 個 .h 頭文件和 1 個 .lib 庫文件整體約 11KB體積輕量、類型簡潔便于直接放入項目目錄并按需配置包含路徑與鏈接依賴。已有 3591 人學(xué)習(xí)或下載說明其在 C 圖形編程入門場景中具有一定參考價值。頭文件可用于了解窗口初始化、坐標(biāo)設(shè)置、基本圖形繪制、顏色管理、文本輸出及事件處理等接口聲明庫文件則承載對應(yīng)預(yù)編譯實現(xiàn)配合 initgraph、rectangle、line、outtextxy 等典型調(diào)用讀者可據(jù)此搭建簡單的圖形窗口與繪圖示例理解圖形庫的鏈接方式、基礎(chǔ) API 使用思路以及圖形會話的初始化與關(guān)閉流程。1. 一個 .rar 里的 graphics.lib把 C 畫圖從控制臺黑框拉到能看見的窗口第一次做 C 課程設(shè)計的人卡點往往不在算法而在屏幕上怎么出現(xiàn)一條線。控制臺只能 printf 字符換到框架又要先配一整套工程兩周工期根本不夠折騰。這個下載包里最核心的就是兩樣?xùn)|西一個graphics.h頭文件和一個graphics.lib靜態(tài)庫接口是 BGI 風(fēng)格的老派圖形 API——initgraph開窗口、line畫線、outtextxy寫字、getmessage收鼠標(biāo)鍵盤事件一個main函數(shù)就能跑出帶界面的結(jié)果。它適合趕課設(shè)、做圖形學(xué)入門作業(yè)、寫小型可視化 Demo 的人也適合想把繪制循環(huán)攥在自己手里、而不是被框架回調(diào)牽著走的熟手。下面按拆包、掛接、繪圖循環(huán)、交互、排錯的順序把它拆成能照著復(fù)現(xiàn)的步驟。2. 拆包與掛接graphics.h 與 graphics.lib 在工程里怎么落地先把選型理由說清楚不然后面每一步都會覺得別扭。這套東西本質(zhì)上是一個薄封裝它把 Windows 下的窗口創(chuàng)建、GDI 繪圖調(diào)用、消息循環(huán)壓縮成十幾個函數(shù)用一套過程式的 API 暴露出來。相比直接寫 Win32 SDK你省掉兩百行窗口過程代碼相比上 Qt 或其它框架你省掉編譯一整棵依賴樹的時間。代價是它的能力邊界很窄——沒有控件、沒有布局、沒有硬件加速畫幾千個圖元還行做動畫粒子多了就會掉幀。判斷標(biāo)準(zhǔn)很簡單你要的是屏幕上能畫、能點、能動選它你要的是做一個正經(jīng)桌面軟件別選它。2.1 目錄怎么擺include 與 lib 分離我不喜歡把.h和.lib跟源碼混在一個目錄里工程大了以后自己都分不清哪個頭文件是第三方帶進來的。常見做法是按下圖這種三層結(jié)構(gòu)擺放源碼、頭文件、庫文件各歸各位project/ ├── include/ │ └── graphics.h # 第三方頭文件只讀不放源碼 ├── lib/ │ └── graphics.lib # 靜態(tài)庫或?qū)霂?├── src/ │ └── main.cpp └── CMakeLists.txt這樣擺的好處是編譯器的-I和鏈接器的-L各指一個目錄換機器或者打包給別人時路徑規(guī)則不用改。參數(shù)說明include/對應(yīng)編譯器頭文件搜索路徑lib/對應(yīng)鏈接器庫搜索路徑兩者不能混用——把頭文件丟進lib/目錄編譯器不會去那里找.h報的錯是fatal error: graphics.h: No such file or directory很多人第一反應(yīng)是庫壞了其實只是路徑?jīng)]指對。還有一件事必須先確認(rèn)這個.lib到底是靜態(tài)庫還是導(dǎo)入庫。判斷方法很笨但有效——如果壓縮包里除了.lib還有一個同名的.dll那.lib多半是導(dǎo)入庫程序運行時必須把.dll拷到.exe同目錄如果只有.lib那就是靜態(tài)庫鏈接進去就完事產(chǎn)物是單個可執(zhí)行文件拷走就能跑。這一步?jīng)]搞清后面會出現(xiàn)我這邊能跑交上去老師那邊雙擊沒反應(yīng)的經(jīng)典事故。提示靜態(tài)庫和導(dǎo)入庫的鏈接參數(shù)完全一樣區(qū)別只在運行期。交付前把.exe單獨拷到一個空目錄雙擊一次能起來才算真的沒問題。2.2 命令行鏈接g 與 MSVC 兩套寫法先用命令行編譯一次比直接開 IDE 更能看清到底鏈接了什么。g 這一套MinGW-w64 環(huán)境大致是這樣g src/main.cpp -o demo.exe \ -I./include \ -L./lib \ -lgraphics -lgdi32 -luser32 -limm32 \ -stdc17 -municode逐項說明-I./include告訴編譯器去哪找graphics.h-L./lib告訴鏈接器去哪找graphics.lib-lgraphics是庫名注意 g 的規(guī)則是-l后面跟去掉lib前綴和.lib/.a后綴的名字所以graphics.lib對應(yīng)-lgraphics寫成-lgraphics.lib會直接報找不到。-lgdi32 -luser32 -limm32是這套圖形接口在底層依賴的系統(tǒng)庫GDI 負責(zé)實際繪制user32 負責(zé)窗口和消息imm32 負責(zé)輸入法消息。漏掉其中任何一個報錯都是未定義引用且錯誤信息里出現(xiàn)的函數(shù)名你根本沒在自己代碼里寫過——這是最容易被誤判成庫版本不對的一類問題。-municode只在頭文件聲明的是寬字符版入口時加加了還報undefined reference to WinMain就把這個參數(shù)去掉再試。MSVC 的cl寫法邏輯一樣語法不同cl /EHsc /std:c17 /I include src\main.cpp ^ /link /LIBPATH:lib graphics.lib gdi32.lib user32.lib imm32.lib注意/I和/link的分界/I屬于編譯器選項/link之后全部交給鏈接器/LIBPATH:是鏈接器的庫搜索路徑。把graphics.lib寫在/link前面cl會把它當(dāng)源文件處理報cannot open input file。這個坑我在第一次配環(huán)境時踩過排查了半小時才發(fā)現(xiàn)是參數(shù)位置問題。2.3 CMake 工程化把鏈接參數(shù)寫進 CMakeLists命令行能跑通之后建議立刻轉(zhuǎn)成 CMake不然每換一臺機器就要重敲一遍參數(shù)出錯概率極高。一份最小可用的CMakeLists.txtcmake_minimum_required(VERSION 3.16) project(gfx_demo CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 頭文件目錄只影響編譯階段 include_directories(${CMAKE_SOURCE_DIR}/include) # 庫目錄只影響鏈接階段 link_directories(${CMAKE_SOURCE_DIR}/lib) add_executable(gfx_demo src/main.cpp) # 私有鏈接graphics 是主庫后面三個是它依賴的系統(tǒng)庫 target_link_libraries(gfx_demo PRIVATE graphics gdi32 user32 imm32)邏輯說明include_directories和link_directories是全局作用域的老寫法小工程夠用工程再大一點應(yīng)該換成target_include_directories和target_link_libraries里的完整路徑避免污染其它 target。PRIVATE的意思是這個依賴不向使用gfx_demo的下游目標(biāo)傳遞對可執(zhí)行文件來說永遠該用PRIVATE。一個容易忽略的點link_directories在 CMake 里必須在add_executable之前調(diào)用才生效而且不同生成器Ninja 與 Visual Studio 生成器對它的處理略有差異。如果 CMake 配置成功了但鏈接還是找不到庫直接把.lib的絕對路徑寫進target_link_libraries最穩(wěn)target_link_libraries(gfx_demo PRIVATE ${CMAKE_SOURCE_DIR}/lib/graphics.lib gdi32 user32 imm32)絕對路徑寫法犧牲了一點可移植性但換來的是排錯時不用懷疑是不是路徑又沒生效。在把工程交給別人之前我一般先保證它能跑再考慮優(yōu)雅。3. 從 initgraph 到 FlushBatchDraw一個不閃爍的繪制循環(huán)掛接成功只是第一步真正決定體驗的是繪制循環(huán)怎么組織。這一章按開窗口 → 畫東西 → 刷新 → 收尾的順序走一遍每一步都給出可跑的代碼和參數(shù)含義。3.1 initgraph 的三個參數(shù)與窗口坐標(biāo)系#include graphics.h int main() { // 640x480 的窗口第三個參數(shù)是擴展標(biāo)志位一般傳 0 或直接省略 initgraph(640, 480, 0); setbkcolor(WHITE); // 設(shè)置背景色注意不會立即生效 cleardevice(); // 用當(dāng)前背景色清屏這一步才真正刷上去 setlinecolor(BLACK); line(0, 0, 639, 479); // 從左上角畫到右下角 getchar(); // 停住窗口等待一次輸入 closegraph(); // 釋放資源、關(guān)閉窗口 return 0; }參數(shù)說明initgraph(width, height, flag)的前兩個參數(shù)是客戶區(qū)寬高不含標(biāo)題欄和邊框所以傳 640×480 得到的窗口在屏幕上會比 640×480 略大。第三個參數(shù)在不同分支里含義不一致有的版本用作樣式標(biāo)志有的版本干脆重載成兩參數(shù)版本——最可靠的做法是打開graphics.h看函數(shù)聲明別照著網(wǎng)上抄。setbkcolor只改內(nèi)部狀態(tài)必須跟一次cleardevice()才能看到效果這是新手最容易困惑的一處明明設(shè)了背景色窗口還是黑的。坐標(biāo)系默認(rèn)原點在左上角x 向右遞增y 向下遞增這跟數(shù)學(xué)課本的直角坐標(biāo)系是反的。如果做函數(shù)圖像、物理軌跡這類東西setorigin(x, y)可以把原點挪到窗口中心之后畫的就是標(biāo)準(zhǔn)笛卡爾坐標(biāo)setorigin(320, 240); // 原點移到窗口中心 setaspectratio(1, -1); // y 軸翻轉(zhuǎn)向上為正 line(-100, 0, 100, 0); // 現(xiàn)在畫出來是一條水平線y0 在窗口正中setaspectratio(1, -1)里的-1是把 y 軸方向鏡像過來。這兩個函數(shù)必須成對使用只調(diào)setorigin不調(diào)setaspectratio圖像會整體上下顛倒畫正弦曲線得到的是反的排查起來很費時間。3.2 顏色、線型與基本圖元顏色用RGB(r, g, b)宏自己算也可以用預(yù)定義常量。線型通過setlinestyle控制填充靠setfillcolor配fill*系列函數(shù)setlinecolor(RGB(0, 120, 215)); setlinestyle(PS_SOLID, 2); // 實線線寬 2 像素 rectangle(50, 50, 250, 200); // 只描邊不填充 setfillcolor(RGB(255, 200, 0)); fillcircle(400, 150, 60); // 有邊框有填充的圓 solidcircle(400, 300, 40); // 只有填充無邊框 // 多邊形先給點數(shù)組再一次性畫 POINT pts[3] { {100, 400}, {200, 300}, {300, 400} }; setfillcolor(RGB(120, 200, 120)); fillpolygon(pts, 3);參數(shù)說明setlinestyle(style, thickness)第一個參數(shù)是線型枚舉PS_SOLID實線、PS_DASH虛線、PS_DOT點線第二個參數(shù)是線寬單位像素但虛線類線型在多數(shù)實現(xiàn)里線寬只能是 1傳 2 會靜默退化成實線——這個行為沒有報錯只能靠肉眼發(fā)現(xiàn)。fillpolygon(POINT*, int)第二個參數(shù)是頂點個數(shù)數(shù)組必須至少這么多元素越界不會崩但會畫出詭異的形狀。rectangle和fillrectangle的區(qū)別值得單獨記一句前者只描邊后者只填充想要填充加描邊必須兩個都調(diào)順序是先fillrectangle再rectangle反過來邊框會被填充色蓋住一半。這個細節(jié)在畫柱狀圖、按鈕這類元素時天天用到。3.3 雙緩沖BeginBatchDraw 三件套的調(diào)用時機直接在循環(huán)里cleardevice()再逐個畫圖元畫面一定閃。原因是每次繪圖調(diào)用都直接寫到了窗口表面清屏和重繪之間存在一個肉眼可見的時間窗。解決辦法是批量繪制initgraph(800, 600); BeginBatchDraw(); // 開啟批量模式之后的繪制先寫進內(nèi)存緩沖 int x 0; while (x 800) { cleardevice(); // 清的是后臺緩沖用戶看不見 setfillcolor(RGB(220, 60, 60)); solidcircle(x, 300, 30); FlushBatchDraw(); // 把整幀一次性貼到窗口上 x 4; Sleep(16); // 約 60 幀/秒 } EndBatchDraw(); // 必須調(diào)用否則緩沖資源不釋放 closegraph();邏輯說明BeginBatchDraw()之后所有繪制都落在離屏緩沖上FlushBatchDraw()是唯一把緩沖內(nèi)容提交到屏幕的動作EndBatchDraw()收尾并釋放緩沖。三者必須成對只寫前兩個不寫最后一個程序退出時會殘留資源FlushBatchDraw放在循環(huán)里一幀只調(diào)一次調(diào)多次等于白開雙緩沖。一個血淚經(jīng)驗FlushBatchDraw之后再調(diào)用cleardevice清的是已經(jīng)提交的畫面會造成單幀閃爍正確順序永遠是清屏 → 畫完全部元素 → 提交。如果畫面里有一部分靜態(tài)內(nèi)容比如坐標(biāo)軸、標(biāo)題欄不想每幀重畫可以只清動態(tài)區(qū)域用setfillcolor配solidrectangle覆蓋舊位置比整體清屏快得多。3.4 關(guān)閉窗口與資源回收closegraph()負責(zé)銷毀窗口、釋放 GDI 對象、恢復(fù)正??刂婆_。放在main的 return 之前或者放在每個提前退出的分支里。如果程序里有return直接從循環(huán)中跳出而沒走closegraph窗口會殘留到進程結(jié)束調(diào)試時看到關(guān)不掉的黑窗口多半就是這個原因。穩(wěn)妥的寫法是把繪圖主體放進一個函數(shù)initgraph和closegraph成對出現(xiàn)在同一個作用域內(nèi)void run() { initgraph(800, 600); // ... 全部繪制邏輯 ... closegraph(); } int main() { run(); return 0; }這樣即便中間拋異常?;赝藭r也至少保證run里的局部對象被析構(gòu)。嚴(yán)格來說closegraph不會因為異常自動執(zhí)行需要 RAII 包裝才能做到萬無一失但對課程設(shè)計這個量級成對寫在一個函數(shù)里已經(jīng)能擋住九成問題。4. 交互接進來鼠標(biāo)鍵盤消息、文字渲染與幀率控制畫面動起來之后下一步是能響應(yīng)操作。這一章講三個東西怎么取消息、怎么寫中文、怎么控制節(jié)奏。三者疊在一起才是一個能演示的程序。4.1 getmessage 與 peekmessage 的區(qū)別這類圖形庫提供兩套取消息的方式行為完全不同選錯了程序要么卡死要么狂占 CPU。ExMessage msg; // 阻塞式?jīng)]有消息就停在這里等適合等用戶點一下再繼續(xù)的場景 while (getmessage(msg)) { if (msg.message WM_LBUTTONDOWN) { // msg.x / msg.y 是相對窗口客戶區(qū)的坐標(biāo) setfillcolor(RGB(0, 160, 220)); solidcircle(msg.x, msg.y, 20); FlushBatchDraw(); } if (msg.message WM_KEYDOWN msg.vkcode VK_ESCAPE) break; // ESC 退出 }getmessage在無消息時會阻塞所以它可以直接當(dāng)主循環(huán)用——程序閑下來就停在那里CPU 占用接近零。代價是動畫沒法做因為一旦進到這個函數(shù)就不返回除非有輸入。需要邊動畫邊響應(yīng)就得換成非阻塞的int x 0; BeginBatchDraw(); while (true) { // 先把消息隊列抽干一條不留 while (peekmessage(msg)) { if (msg.message WM_KEYDOWN msg.vkcode VK_ESCAPE) goto done; if (msg.message WM_LBUTTONDOWN) x msg.x; // 點哪兒球就跳到哪兒 } cleardevice(); setfillcolor(RGB(220, 60, 60)); solidcircle(x, 300, 30); FlushBatchDraw(); x (x 3) % 800; Sleep(16); } done: EndBatchDraw(); closegraph();參數(shù)說明peekmessage(msg)取一條消息就返回隊列空時返回 false不阻塞getmessage(msg)隊列空時掛起等待。兩個函數(shù)都可以帶第二個參數(shù)做消息過濾比如只取鼠標(biāo)消息傳EX_MOUSE只取鍵盤傳EX_KEY過濾能減少無謂的判斷。注意peekmessage的 while 循環(huán)必須放在每幀開頭并且抽干如果只取一條就開始畫消息會積壓表現(xiàn)為點了很多下只有第一下生效。ExMessage結(jié)構(gòu)里的字段要看頭文件確認(rèn)常用的是message消息類型、xy鼠標(biāo)坐標(biāo)、lbuttonrbutton按鍵狀態(tài)、vkcode虛擬鍵碼、wheel滾輪增量。4.2 文字輸出字體、對齊與中文亂碼寫字用outtextxy但字體、大小、顏色都要提前設(shè)settextcolor(RGB(30, 30, 30)); setbkmode(TRANSPARENT); // 背景透明否則文字帶一塊底色 settextstyle(24, 0, L微軟雅黑); // 高度 24寬度自適應(yīng) outtextxy(20, 20, L幀率: 60 FPS); outtextxy(20, 60, L點擊畫布放置圓點);參數(shù)說明settextstyle(height, width, face)的height是字符高度像素width傳 0 表示按字體比例自適應(yīng)傳具體值會強制拉伸字形導(dǎo)致變形一般不要傳。第三個參數(shù)是字體名必須是寬字符串寫成微軟雅黑在某些配置下會編譯失敗。setbkmode(TRANSPARENT)這一行幾乎不能省默認(rèn)是不透明模式文字背后會帶一塊背景色方塊疊在圖形上非常難看。中文亂碼是這一類項目的高頻故障根源是字符集。源文件存成 UTF-8編譯器按 GBK 解析L中文就變成一堆問號。處理辦法有兩條要么統(tǒng)一源文件編碼和編譯器編碼MSVC 加/utf-8g 加-finput-charsetUTF-8 -fexec-charsetUTF-8要么干脆把界面文字換成英文。趕工期的時候第二條路的性價比往往更高。4.3 幀率Sleep 掐時間還是固定步長上一節(jié)的Sleep(16)是最樸素的限幀手段它的問題是精度差——Windows 下Sleep的實際休眠時間通常比請求值多 1 到 15 毫秒取決于系統(tǒng)計時器分辨率。跑到 60 幀勉強夠想要穩(wěn)定 30 幀以上又不想畫面抖動得自己算時間差#include chrono using namespace std::chrono; auto last steady_clock::now(); double acc 0.0; while (true) { auto now steady_clock::now(); double dt durationdouble(now - last).count(); last now; acc dt; // 邏輯更新用真實時間差不受幀率波動影響 // 這里省略具體更新重點是 dt 這個量 cleardevice(); // ... 繪制 ... FlushBatchDraw(); // 目標(biāo) 16.6ms 一幀超了就補沒超就等 auto cost durationdouble(steady_clock::now() - now).count(); if (cost 0.0166) Sleep(static_castDWORD((0.0166 - cost) * 1000)); }邏輯說明用steady_clock而不是system_clock因為后者會被系統(tǒng)時間調(diào)整影響做動畫計時時必須單調(diào)遞增。dt是上一幀到這一幀的真實秒數(shù)所有跟時間相關(guān)的量位移、旋轉(zhuǎn)角度都乘以它這樣幀率掉下去動畫速度也不會變——這就是所謂幀率無關(guān)。很多課設(shè)里小球在別人電腦上跑得飛快、在自己電腦上慢吞吞就是位移寫成了每幀固定值。注意Sleep(0)不等于不睡它會讓出當(dāng)前時間片在緊循環(huán)里反而可能比Sleep(1)更耗 CPU。真正想跑滿幀就別睡靠垂直同步或者手動判斷時間差。5. 鏈接與運行排查五條最常見翻車記錄這一章全是現(xiàn)象描述按看到什么 → 為什么 → 怎么辦寫。這些坑我在不同機器、不同編譯器上反復(fù)遇到過順序基本按出現(xiàn)頻率排。5.1 現(xiàn)象滿屏未解析的外部符號函數(shù)名自己沒寫過原因鏈接器只拿到了graphics.lib沒拿到它依賴的系統(tǒng)庫。圖形接口內(nèi)部調(diào)用了 GDI、user32 的函數(shù)這些函數(shù)在系統(tǒng)庫的導(dǎo)入庫里不顯式鏈接就找不到。解決按報錯信息里的函數(shù)名前綴判斷缺哪個庫——帶Gdi、CreatePen、BitBlt的補gdi32帶CreateWindow、GetMessage、RegisterClass的補user32帶Imm的補imm32。CMake 里就是target_link_libraries那一行加上去命令行就是-lgdi32 -luser32 -limm32。還有一種情況是所有函數(shù)都報未解析那說明.lib根本沒被鏈接上檢查-L路徑和庫名拼寫。5.2 現(xiàn)象雙擊 exe窗口一閃就沒了原因main執(zhí)行完直接返回了initgraph之后的繪制還沒來得及顯示進程就退出了?;蛘吒[蔽的一種——.lib是導(dǎo)入庫運行時找不到配套的.dll加載階段就失敗窗口創(chuàng)建都來不及。解決先確認(rèn)代碼里有沒有讓程序停住的東西最土但有效的辦法是在closegraph()前加一句getchar()或者在主循環(huán)里加一個等待 ESC 的分支。排除代碼原因后把.dll拷到.exe同目錄再試。判斷是不是 DLL 缺失可以打開命令行在 exe 目錄下運行一次缺失時系統(tǒng)會彈一個明確的錯誤框。5.3 現(xiàn)象畫面劇烈閃爍尤其是有大量圖元的時候原因沒有開雙緩沖或者開了但調(diào)用順序錯了。還有一種情況是每幀都在setbkcolor之后再cleardevice等于每幀多刷一次全屏。解決確認(rèn)BeginBatchDraw在循環(huán)外調(diào)一次FlushBatchDraw在循環(huán)內(nèi)調(diào)一次EndBatchDraw在退出前調(diào)一次。再檢查清屏和繪制的順序必須是清 → 畫 → 提交。如果圖元數(shù)量上千光靠雙緩沖也不夠得減少每幀重繪面積把靜態(tài)部分畫到一張內(nèi)存圖像上反復(fù)貼。5.4 現(xiàn)象中文顯示成亂碼、問號或者方框原因三種可能一是源文件編碼和編譯器輸入編碼不一致二是用了窄字符版本的輸出函數(shù)卻傳了中文三是字體名指定的字體系統(tǒng)里沒有回退到默認(rèn)字體后中文缺字形。解決先統(tǒng)一編碼源文件保存為 UTF-8編譯加/utf-8或-finput-charsetUTF-8再確認(rèn)outtextxy的字符串是L...寬字符形式最后換一個確定存在的中文字體或者干脆先用英文把功能跑通再回頭處理字體問題。方框和問號要區(qū)分開——問號是編碼問題方框是字體缺字形兩種故障的修法完全不同。5.5 現(xiàn)象32 位配置能編過切到 64 位鏈接失敗原因.lib是 32 位的跟 64 位的編譯目標(biāo)架構(gòu)不匹配。靜態(tài)庫對架構(gòu)敏感位數(shù)不一致時鏈接器會直接報模塊計算機類型不匹配。解決把工程配置切回 x8632 位再編。如果非要 64 位就得找對應(yīng)架構(gòu)的庫文件沒有的話只能改配置——在項目設(shè)置里把目標(biāo)平臺改成 Win32CMake 里就是cmake -A Win32 ..。這個錯在換電腦、換 IDE 默認(rèn)配置時特別容易出現(xiàn)因為很多模板默認(rèn)建的是 64 位工程。提示排錯時先看報錯發(fā)生在哪個階段。編譯階段報錯是頭文件和語法問題鏈接階段報錯是庫和架構(gòu)問題運行階段崩潰是資源和邏輯問題。三個階段混在一起猜效率會低很多。6. 驗收一個繪圖 Demo三條線跑通再交付寫完代碼不等于能交。我習(xí)慣用一個最小驗收清單過一遍三條線繪制、交互、幀率每條都有可觀察的判據(jù)不靠感覺。檢查項判據(jù)不合格的典型表現(xiàn)繪制完整性靜態(tài)元素每幀都在正確位置無殘留拖影、舊圖元沒被覆蓋雙緩沖生效快速移動的物體邊緣干凈邊緣有鋸齒狀撕裂交互響應(yīng)連續(xù)點擊十次十次都有反應(yīng)只響應(yīng)第一次退出路徑ESC、點關(guān)閉按鈕、AltF4 都能正常退進程殘留、黑窗口關(guān)不掉獨立運行exe 拷到空目錄雙擊能起換機器就報缺文件中文顯示標(biāo)題、按鈕文字無問號無方框全是???驗收里最容易被跳過的是獨立運行這一條。在自己機器上一切正常是因為開發(fā)環(huán)境的 PATH 里恰好有需要的 DLL而別人機器上沒有。我的做法是每次改完代碼先把build目錄里的 exe 單獨拷到桌面一個空文件夾雙擊跑一遍能起來再繼續(xù)。這一步花不了一分鐘但攔下過好幾次交付前才發(fā)現(xiàn)的問題。再往上一層如果你的 Demo 要交給別人繼續(xù)改建議把窗口尺寸、顏色、幀間隔這些數(shù)字全部提到文件頭部做成常量別散落在代碼中間const int WIN_W 800; const int WIN_H 600; const int FRAME_MS 16; // 目標(biāo)幀間隔 const COLORREF BG RGB(245, 245, 245); const COLORREF FG RGB(220, 60, 60);這樣別人調(diào)參不用滿文件搜索數(shù)字也避免改了一處漏了另一處??雌饋硎切∈碌粋€散落著魔數(shù)的繪圖程序接手的人第一件事就是重寫。最后說一個我自己的習(xí)慣。凡是用這類庫做的東西我一定會在main的第一行加一句日志輸出把窗口尺寸和庫的路徑打出來closegraph之前再打一句正常退出。聽著很笨但程序閃退時這兩行輸出能立刻區(qū)分根本沒進主流程和跑到一半崩了比開調(diào)試器快。從那以后我每次配新環(huán)境都強制走一遍命令行編譯 → 空目錄雙擊 → 看兩行日志的流程沒再出現(xiàn)過交付當(dāng)天才發(fā)現(xiàn)鏈接不上這種事。希望幫到你。本文還有配套的精品資源點擊獲取