實(shí)踐)
開發(fā)工具靜態(tài)分析代碼質(zhì)量質(zhì)量保障【免費(fèi)下載鏈接】cppcheckstatic analysis of C/C code項(xiàng)目地址https://gitcode.com/gh_mirrors/cpp/cppcheck點(diǎn)擊查看免費(fèi)下載本文以 cppcheck 官方檢查器文檔 man/checkers/uninitMemberVar.md 為主體深入講解uninitMemberVar及其派生出的七個(gè)兄弟告警 ID 的含義、觸發(fā)條件與修復(fù)方法并結(jié)合 lib/checkclass.cpp 的源碼實(shí)現(xiàn)與 test/testconstructors.cpp 的測(cè)試用例還原 cppcheck 如何追蹤成員變量從未被賦值這一未定義行為。讀完本文你將掌握八類告警的命名規(guī)律、判定邏輯、CWE 歸屬與可落地的初始化修復(fù)方案。告警總覽uninitMemberVar是 cppcheck 的 C 檢查項(xiàng)之一用于發(fā)現(xiàn)構(gòu)造函數(shù)或拷貝/移動(dòng)構(gòu)造函數(shù)從未給某個(gè)成員變量賦值的問題。它的官方描述如下MessageMember variable x is not initialized in the constructor.CategoryUndefined Behaviour未定義行為SeverityWarning警告LanguageCCWECWE-398由 lib/checkclass.cpp 中的reportError(..., CWE398, ...)調(diào)用可確認(rèn)值得注意的是這一底層發(fā)現(xiàn)實(shí)際上被拆分為八個(gè)不同的告警 IDuninitMemberVar、uninitMemberVarPrivate、uninitDerivedMemberVar、uninitDerivedMemberVarPrivate、uninitMemberVarNoCtor、uninitMemberVarPrivateNoCtor、uninitDerivedMemberVarNoCtor、uninitDerivedMemberVarPrivateNoCtor。八個(gè) ID 指向的是同一個(gè)底層問題只是根據(jù)場(chǎng)景的三個(gè)獨(dú)立維度做了細(xì)分。八類 ID 的命名規(guī)則與組合邏輯八個(gè) ID 由三個(gè)獨(dú)立、可疊加的場(chǎng)景維度組合而成每個(gè)維度對(duì)應(yīng) ID 中的一個(gè)標(biāo)記片段1.Derived未初始化成員屬于基類當(dāng)未初始化的成員變量屬于基類base class而非正在審查構(gòu)造函數(shù)所在的這個(gè)類時(shí)ID 中會(huì)出現(xiàn)Derived。也就是說(shuō)派生類的構(gòu)造函數(shù)沒有初始化從基類繼承來(lái)的成員而該成員本身又沒有任何默認(rèn)值。2.Private無(wú)構(gòu)造函數(shù)但部分成員有默認(rèn)值或構(gòu)造函數(shù)本身是私有的Private標(biāo)記出現(xiàn)在兩種情形中類完全沒有構(gòu)造函數(shù)且只有部分成員擁有類內(nèi)默認(rèn)值in-class default initializer——此時(shí)沒有默認(rèn)值的成員會(huì)以帶Private的 ID 被標(biāo)記缺少初始化的那個(gè)構(gòu)造函數(shù)本身是private的。3.NoCtor類沒有構(gòu)造函數(shù)但部分成員有類內(nèi)默認(rèn)初始化NoCtor標(biāo)記出現(xiàn)在類完全沒有構(gòu)造函數(shù)、但部分成員確實(shí)帶有類內(nèi)默認(rèn)初始化器的場(chǎng)景。cppcheck 的邏輯是既然作者已經(jīng)為某些成員寫了默認(rèn)初始化說(shuō)明作者是想過(guò)初始化這件事的那么任何沒有默認(rèn)值的其余成員就值得被標(biāo)記出來(lái)。8 種組合與 ID 拼裝規(guī)則三個(gè)維度可組合出 23 8 種 ID。其拼裝規(guī)則在 lib/checkclass.cpp 的uninitVarError中有最直接的體現(xiàn)std::string id std::string(uninit) (derived ? Derived : ) MemberVar (isprivate ? Private : ) (noCtor ? NoCtor : );即uninit 可選DerivedMemberVar 可選Private 可選NoCtor。其中三種組合uninitMemberVarPrivateNoCtor、uninitDerivedMemberVarNoCtor、uninitDerivedMemberVarPrivateNoCtor在當(dāng)前版本中僅理論上可能存在——從 lib/checkclass.cpp 的實(shí)際代碼路徑看沒有任何一條路徑會(huì)同時(shí)拼出這三組 ID 字符串因此實(shí)踐中不會(huì)看到它們。為什么值得修復(fù)未初始化的成員是未定義行為檢查器的 Motivation動(dòng)機(jī)非常明確在成員變量從未被賦值前就讀取它屬于未定義行為undefined behaviour——無(wú)論該類是否管理任何資源該成員的取值都只是那塊內(nèi)存里恰好殘留的任意字節(jié)。這意味著行為不可預(yù)測(cè)不同平臺(tái)、不同編譯選項(xiàng)下結(jié)果可能完全不同難以復(fù)現(xiàn)、難以調(diào)試是典型的潛伏性 bug對(duì)指針、引用、原生類型native types成員尤其危險(xiǎn)。lib/checkclass.cpp 的 verbose 提示信息補(bǔ)充說(shuō)明了影響面Member variables of native types, pointers, or references are left uninitialized when the class is instantiated. That may cause bugs or undefined behavior.原生類型、指針或引用類型的成員在類實(shí)例化時(shí)未被初始化可能引發(fā) bug 或未定義行為。修復(fù)方法給每個(gè)成員一個(gè)初始值官方給出的修復(fù)原則只有一條給每個(gè)成員賦值——要么通過(guò)類內(nèi)默認(rèn)初始化器in-class default initializer要么在每個(gè)構(gòu)造函數(shù)的成員初始化列表member-initializer list中完成。場(chǎng)景一構(gòu)造函數(shù)體為空成員從未賦值// Before class Fred { public: Fred() {} // - i is never given a value int i; }; // After class Fred { public: Fred() : i(0) {} int i; };場(chǎng)景二無(wú)構(gòu)造函數(shù)但部分成員有默認(rèn)值// Before class C { private: int i1 0; int i2; // - no constructor, and i2 has no default like i1 does }; // After class C { private: int i1 0; int i2 0; };場(chǎng)景三派生類未初始化基類成員// Before class Base { public: virtual void foo() 0; int x; // - left uninitialized by every class that derives from Base }; class Derived: public Base { public: Derived() {} void foo() override; }; // After class Base { public: Base() : x(0) {} virtual void foo() 0; int x; }; class Derived: public Base { public: Derived() {} void foo() override; };場(chǎng)景四基類的私有構(gòu)造函數(shù)未初始化自己的成員// Before class B { int i; }; // - Bs own (private) constructor doesnt init i class D : B { explicit D(int) {} }; // After class B { int i; public: B() : i(0) {} }; class D : B { explicit D(int) {} };場(chǎng)景五結(jié)構(gòu)體中部分成員有默認(rèn)值// Before struct S { int a 0, b; // - a has a default, b doesnt }; // After struct S { int a 0, b 0; };源碼級(jí)剖析cppcheck 是如何找到這些成員的從源碼結(jié)構(gòu)看這一組檢查的全部核心邏輯集中在 lib/checkclass.cpp 的CheckClassImpl::constructors()函數(shù)中屬于CheckClass檢查器對(duì)所有類構(gòu)造函數(shù)是否合理這一主題的檢查代碼注釋為ClassCheck: Check that all class constructors are ok。無(wú)構(gòu)造函數(shù)分支Private / NoCtor 的產(chǎn)生當(dāng)類完全沒有構(gòu)造函數(shù)scope-numConstructors 0時(shí)代碼會(huì)先統(tǒng)計(jì)需要初始化的私有成員needInit與已有默認(rèn)值或初始化的成員haveInit見 lib/checkclass.cpp如果沒有任何成員擁有默認(rèn)初始化haveInit 0則走noConstructorError()報(bào)告的是另一個(gè)相關(guān)檢查器noConstructorstyle 級(jí)別而不是本組告警如果部分成員有默認(rèn)值、部分沒有則對(duì)沒有默認(rèn)值的私有成員調(diào)用uninitVarError(uv-typeStartToken(), uv-scope()-className, uv-name())該重載在 lib/checkclass.cpp 中固定使用 IDuninitMemberVarPrivate消息為Member variable $symbol is not initialized.。無(wú)構(gòu)造函數(shù)但成員有部分默認(rèn)值的NoCtor場(chǎng)景則在 lib/checkclass.cpp 的獨(dú)立分支處理它先檢查是否存在任何類內(nèi)默認(rèn)初始化hasAnyDefaultInit或 C14 及以后的自初始化hasAnySelfInit若兩者皆無(wú)則直接跳過(guò)避免過(guò)度告警一旦確認(rèn)作者考慮過(guò)初始化就對(duì)每個(gè)未初始化且非聯(lián)合體成員、且其類型本身沒有構(gòu)造函數(shù)的成員以noCtortrue調(diào)用uninitVarError(...)最終消息變?yōu)镸ember variable $symbol has no initializer.。有構(gòu)造函數(shù)分支普通 / Derived 的產(chǎn)生當(dāng)類存在構(gòu)造函數(shù)時(shí)檢查器會(huì)為每個(gè)構(gòu)造函數(shù)建立成員使用記錄Usage把類內(nèi)默認(rèn)初始化過(guò)的成員標(biāo)記為已初始化再通過(guò)initializeVarList()模擬構(gòu)造函數(shù)的實(shí)際初始化動(dòng)作最后對(duì)仍未初始化的成員逐一判定lib/checkclass.cpp。關(guān)鍵判定點(diǎn)在于derived scope ! var.scope()當(dāng)成員的歸屬作用域與正在檢查的構(gòu)造函數(shù)所屬作用域不同即成員屬于基類時(shí)derived為真ID 中就會(huì)帶上Derivedisprivate來(lái)自func.access AccessControl::Private即該構(gòu)造函數(shù)本身是否為私有消息文本中FunctionType::eCopyConstructor與FunctionType::eMoveConstructor會(huì)分別產(chǎn)出is not initialized in the copy constructor.與is not initialized in the move constructor.見 lib/checkclass.cpp。與 operator 的邊界劃分值得區(qū)分的是本組檢查器只關(guān)心構(gòu)造函數(shù)含拷貝、移動(dòng)構(gòu)造中的未初始化問題。若成員是在賦值運(yùn)算符operator中未被賦值cppcheck 會(huì)走另一條路徑報(bào)告operatorEqVarError見 lib/checkclass.cpp若拷貝/移動(dòng)構(gòu)造函數(shù)忘記拷貝某個(gè)特定成員則報(bào)告missingMemberCopy。三者同屬構(gòu)造函數(shù)/賦值健康檢查主題但定位不同。測(cè)試用例如何驗(yàn)證這組檢查器test/testconstructors.cpp 提供了大量針對(duì)本組告警的回歸測(cè)試可以直接對(duì)照預(yù)期輸出理解每個(gè) ID 的實(shí)際行為空構(gòu)造、空拷貝構(gòu)造、空移動(dòng)構(gòu)造同時(shí)存在時(shí)同一成員i會(huì)分別產(chǎn)生三條uninitMemberVar告警消息中區(qū)分 constructor / copy constructor / move constructor見 test/testconstructors.cpp無(wú)構(gòu)造函數(shù) 部分成員有默認(rèn)值class C { int i1 0; int i2; }產(chǎn)生[uninitMemberVarPrivate]若兩個(gè)成員都沒有默認(rèn)值則只報(bào) style 級(jí)noConstructor見 test/testconstructors.cpp結(jié)構(gòu)體struct S { int a 0, b; }無(wú)構(gòu)造函數(shù)產(chǎn)生[uninitMemberVarNoCtor]而struct S { int a, b; }不告警見 test/testconstructors.cpp派生類場(chǎng)景class Base { virtual void foo() 0; int x; }配合空的派生類構(gòu)造函數(shù)產(chǎn)生[uninitDerivedMemberVar]消息還附帶Maybe it should be initialized directly in the class Base?的修復(fù)建議見 test/testconstructors.cpp基類私有構(gòu)造函數(shù) 派生類場(chǎng)景class B { int i; }、class D : B { explicit D(int) {} }產(chǎn)生[uninitDerivedMemberVarPrivate]見 test/testconstructors.cpp 附近。這些用例說(shuō)明本組檢查器對(duì)default 的成員有默認(rèn)值、拷貝構(gòu)造仍需復(fù)制等語(yǔ)義細(xì)節(jié)做了專門處理例如代碼中var.hasDefault() func.type ! FunctionType::eCopyConstructor的判斷l(xiāng)ib/checkclass.cpp確保類內(nèi)默認(rèn)值 拷貝構(gòu)造的組合不會(huì)被誤報(bào)為未初始化。運(yùn)行配置與輸出形態(tài)啟用條件本組告警屬于 warning 級(jí)別輸出時(shí)以(warning)標(biāo)注同時(shí)受--enablewarning或默認(rèn)級(jí)別控制。在 lib/checkclass.cpp 中若 style 與 warning 級(jí)別均未啟用、且未開啟 premium 配置項(xiàng)uninitMemberVar整個(gè)檢查函數(shù)會(huì)提前返回。inconclusive 模式部分場(chǎng)景如類存在多個(gè)構(gòu)造函數(shù)、用戶自定義的默認(rèn)構(gòu)造函數(shù)等邊界情況只有在--inconclusive開啟時(shí)才會(huì)以(warning, inconclusive)形態(tài)報(bào)告對(duì)應(yīng) lib/checkclass.cpp 中的printInconclusive判定。CWE 歸屬所有八個(gè) ID 統(tǒng)一映射到 CWE-398Indicator of Poor Code Quality消息格式為Member variable Class::member is not initialized in the constructor. [ID]。檢查器注冊(cè)u(píng)ninitMemberVar等 ID 出現(xiàn)在 lib/settings.cpp 的檢查器列表中與uninitvar、uninitdata等未初始化相關(guān)檢查并列可用于--checkers-report等工具輸出查看。實(shí)際運(yùn)行在命令行中執(zhí)行cppcheck --enablewarning yourfile.cpp即可觸發(fā)本組檢查是 C 專屬Language: C對(duì) C 代碼不適用。相關(guān)檢查器noConstructor.md當(dāng)類完全沒有構(gòu)造函數(shù)時(shí)對(duì)應(yīng)的檢查style 級(jí)別ID 為noConstructor與本組中的uninitMemberVarPrivate/uninitMemberVarNoCtor互為補(bǔ)充——前者針對(duì)沒有任何成員被初始化后者針對(duì)部分成員有默認(rèn)值。missingMemberCopy.md拷貝/移動(dòng)構(gòu)造函數(shù)忘記拷貝某個(gè)特定成員而非從未初始化它時(shí)的同類發(fā)現(xiàn)ID 為missingMemberCopy與本組告警的觸發(fā)路徑相鄰lib/checkclass.cpp 中missingCopy分支。贊分享開發(fā)工具靜態(tài)分析代碼質(zhì)量質(zhì)量保障【免費(fèi)下載鏈接】cppcheckstatic analysis of C/C code項(xiàng)目地址https://gitcode.com/gh_mirrors/cpp/cppcheck點(diǎn)擊查看免費(fèi)下載相關(guān)推薦Node.js 2017 年 12 月安全公告深度解讀CVE-2017-15896 數(shù)據(jù)機(jī)密性/完整性漏洞與緩沖區(qū)未初始化漏洞修復(fù)Node.js 2017 年 12 月安全公告深度解讀CVE 2017 15896 數(shù)據(jù)機(jī)密性/完整性漏洞與緩沖區(qū)未初始化漏洞修復(fù) 2017 年 12 月N開發(fā)工具靜態(tài)分析代碼質(zhì)量質(zhì)量保障Home Assistant 配置完整指南YAML 四步通關(guān) UI 快捷操作附坑位速查表Home Assistant 配置完整指南YAML 四步通關(guān) UI 快捷操作附坑位速查表 這篇文章給你兩套最落地的 Home Assistant 配置開發(fā)工具靜態(tài)分析代碼質(zhì)量質(zhì)量保障cppcheck noConstructor 檢查器深度解析檢測(cè)無(wú)構(gòu)造函數(shù)的類中未初始化私有成員cppcheck noConstructor 檢查器深度解析檢測(cè)無(wú)構(gòu)造函數(shù)的類中未初始化私有成員 導(dǎo)讀 noConstructor 是 cppcheck開發(fā)工具靜態(tài)分析代碼質(zhì)量質(zhì)量保障上一篇如何5分鐘掌握Vosk離線語(yǔ)音識(shí)別零依賴多語(yǔ)言AI轉(zhuǎn)文字終極指南下一篇Domino測(cè)試策略如何利用W3C標(biāo)準(zhǔn)測(cè)試確保DOM實(shí)現(xiàn)正確性創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考