资讯动态

cppcheck 检查器详解:danglingTemporaryLifetime —— 识别指向已销毁临时对象的悬垂指针与迭代器

发布时间:2026/10/4 14:25:16 来源:尧图企业网站定制
开发工具静态分析代码质量质量保障【免费下载链接】cppcheckstatic analysis of C/C code项目地址https://gitcode.com/gh_mirrors/cpp/cppcheck点击查看免费下载导读danglingTemporaryLifetime是 cppcheck 静态分析引擎中用于检测 C悬垂临时对象dangling temporary问题的检查器当程序在临时对象已被销毁之后仍然通过指向它的指针或迭代器访问该对象时它会报告一个 Undefined Behaviour未定义行为级别的错误。本文以 man/checkers/danglingTemporaryLifetime.md 为骨架结合 lib/checkautovariables.cpp 的实现与 test/testautovariables.cpp 的测试用例完整讲解该检查器的触发原理、典型场景、修复策略、底层实现与命令行使用方式帮助你在实际项目中定位并消除这一类极易被忽视的 C 生命周期缺陷。检查器概览属性值检查器 IDdanglingTemporaryLifetime报告消息Using pointer to dangling temporary.分类Undefined Behaviour未定义行为严重级别Error错误适用语言C关联 CWECWE-562Return of Stack Variable Address从源码看该检查器由CheckAutoVariables这一检查族负责。在 lib/checkautovariables.cpp 的runChecks()中checkVarLifetime()被纳入主流程专门负责变量与临时对象的生命周期合法性检查。其对应的错误报告函数为 errorDanglingTemporaryLifetime()报告时使用Severity::error、CWE562并根据 ValueFlow 推断的确定性决定Certainty::normal或Certainty::inconclusive。问题本质为什么指向临时对象的指针会悬垂临时对象的默认销毁时机C 标准规定一个临时对象temporary通常会在创建它的完整表达式full expression结束时被销毁。例如const char* p std::string(hello).c_str(); // hello 这个临时 std::string 在分号处完整表达式结束就被销毁了 // p 从此成为悬垂指针之后任何 *p 都是未定义行为c_str()返回的指针指向std::string内部缓冲区而该缓冲区随临时对象一起销毁。这正是danglingTemporaryLifetime要捕捉的核心场景。引用绑定是例外指针/迭代器不是原文档特别强调了一个易被误解的细节引用绑定reference binding在少数情况下可以延长临时对象的生命周期如const T或T绑定到临时对象时在特定规则下临时对象会活到引用消亡为止而指针或迭代器不具备这种延长生命周期的能力。因此即使代码看起来只是把一个中间结果存了下来只要它是以指针/迭代器形式保存的就依然受完整表达式结束时销毁规则的约束。原文档的结论很明确A pointer or iterator into it doesnt extend its lifetime the way a reference binding sometimes can - so using one afterwards is a use of an already-destroyed object.即临时对象只应在创建它的同一个完整表达式内通过指针/迭代器使用若需要让数据活得更久应改为按值复制或移动。典型触发场景附测试用例佐证danglingTemporaryLifetime在 test/testautovariables.cpp 的danglingTemporaryLifetime()测试中覆盖了大量真实场景。以下场景均可由该检查器命中场景一从成员函数返回的临时对象上取c_str()QString f() { QString a(dummyValue); const char* b a.toStdString().c_str(); // toStdString() 的临时对象在此行结束即销毁 QString c b; // 读取悬垂指针 return c; }测试对应断言testautovariables.cpp报告[test.cpp:3:42] - [test.cpp:3:34] - [test.cpp:4:15]: (error) Using pointer that is a temporary. [danglingTemporaryLifetime]a.toStdString()返回一个临时std::string其.c_str()指针在完整表达式结束时失效随后被用于构造c属于典型悬垂访问。场景二substr()的临时结果上取指针/迭代器auto f(std::string s) { const char *x s.substr(1,2).c_str(); // 临时 std::string 已销毁 auto i s.substr(4,5).begin(); // 临时 std::string 的迭代器同样悬垂 return *i; // 解引用已销毁对象的迭代器 }测试断言testautovariables.cpp报告[test.cpp:3:33] - [test.cpp:3:22] - [test.cpp:4:13]: (error) Using iterator that is a temporary. [danglingTemporaryLifetime]注意这里报告消息自动变为了Using iterator that is a temporary.—— 说明同一检查器会按被悬垂对象的具体种类指针 / 迭代器 / 对象动态生成消息文本。场景三临时对象成员返回的迭代器存入长生命周期变量struct A { std::mapint, int m_; }; struct B { A a_; }; B func(); void f() { const std::mapint, int::iterator m func().a_.m_.begin(); // func() 临时对象已销毁 (void)m-first; // 使用悬垂迭代器 }测试对应断言testautovariables.cppissue #14054证明即使通过多级成员访问func().a_.m_.begin()检查器也能沿 ValueFlow 追踪到func()临时对象的生命周期边界。场景四向临时对象取地址struct A { int x; }; A* g(); void f() { A** ap g(); // g() 得到的是临时对象的地址 (*ap)-x; // 使用悬垂指针 }测试断言testautovariables.cpp报告 Using pointer that is a temporary.。场景五跨函数传递的临时对象std::vectorchar* f(const std::vectorstd::string args) { std::vectorchar* cargs; for (const auto a : args) cargs.push_back(const_castchar*(a.data())); // data() 指向容器内部缓冲区 return cargs; } void g() { std::vectorchar* cargs f({ 0, 0 }); // 花括号初始化的临时 vector 已销毁 (void)cargs; // cargs 中的指针全部悬垂 }测试断言testautovariables.cppissue #9773的 error path 跨越多个函数最终报告 Using object that is a temporary.。场景六lambda 捕获临时对象templateclass T auto f(T x) { return [] { return x(); }; // lambda 按引用捕获参数 } auto g() { auto y f([](auto x) { return 1; }); // 实参为临时 lambda return y(); // 引用已失效的临时对象 }测试断言testautovariables.cppissue #13760证明检查器同样覆盖 lambda 捕获导致的临时对象悬垂。如何修复让数据活过完整表达式原文档给出的修复原则只有两条但覆盖了所有场景仅在创建临时对象的同一个完整表达式内使用其指针/迭代器——例如链式调用foo().bar()、把s.substr(1,2).c_str()的返回值立即传给接收方如U u(p-g().c_str())这类用完即弃的写法见测试 testautovariables.cpp 中 issue #11298 的合法用例。若数据需要活得比该表达式更久按值复制或移动而不是保存指针/迭代器// 修复前悬垂 const char* p std::string(hello).c_str(); // 修复后按值持有数据安全存活 std::string s std::string(hello); const char* p s.c_str(); // p 指向 s 的内部缓冲区随 s 存活迭代器场景同理// 修复前悬垂 auto it s.substr(4,5).begin(); // 修复后先把临时结果存入具名对象 std::string sub s.substr(4,5); auto it sub.begin();测试中还包含大量合法写法不应误报的回归用例例如在同一表达式中立即使用迭代器issue #11609 的m.find(s.substr(1,4))、const S a[] { { i } };的聚合初始化issue #11057、static const std::string指向静态存储期对象issue #11442、以及reinterpret_cast指向非临时对象issue #11472等——这些用例保证了检查器在修正确缺陷的同时不会过度误报。底层实现原理ValueFlow 生命周期追踪入口与扫描范围检查的核心实现在 checkVarLifetimeScope()由 checkVarLifetime() 对符号数据库SymbolDatabase中每个函数作用域调用。扫描范围不仅包含普通函数体还包含 lambda 体findLambdaEndToken后递归进入以及局部类中的成员函数见 lib/checkautovariables.cpp。三类命中路径checkVarLifetimeScope()对每个 token 检查其 ValueFlow 生命周期值val.isLocalLifetimeValue()与val.isSubFunctionLifetimeValue()并结合ValueFlow::getLifetimeTokens()解析出临时对象lifetime token。最终产生danglingTemporaryLifetime报告的关键判断是} else if (!tokvalue-variable() isDeadTemporary(tokvalue, tok, mSettings.library)) { if (!diag(tokvalue)) errorDanglingTemporaryLifetime(tok, val, tokvalue); break; }即lifetime token 不是具名变量没有 variable()且被判定为已死临时对象isDeadTemporary时触发报告。isDeadTemporary()定义于 lib/checkautovariables.cpp结合mSettings.library判断该表达式是否属于活到用完为止的合法临时对象。消息文本的动态生成errorDanglingTemporaryLifetime()的报告消息为Using lifetimeMessage(tok, val, errorPath) that is a temporary.见 lib/checkautovariables.cpplifetimeMessage()按被悬垂实体的类型产出pointer、iterator或object因此你会看到三种消息变体Using pointer that is a temporary.Using iterator that is a temporary.Using object that is a temporary.此外报告还携带error pathTemporary created here.标注临时对象创建点最终使用点标注为报告位置这就是命令行输出中[test.cpp:3:42] - [test.cpp:3:34] - [test.cpp:4:15]这类溯源链的由来。与确定性Certainty的关系当 ValueFlow 推断含不确定性时报告会标记为inconclusiveCertainty::inconclusive并在 checkVarLifetimeScope 中依据printInconclusive由mSettings.certainty.isEnabled(Certainty::inconclusive)决定决定是否打印。开启--inconclusive命令行选项即可让这类推断结果也出现在输出中。命令行使用与输出示例danglingTemporaryLifetime属于 Error 严重级别的检查器位于 cppcheck 默认启用的错误级检查集合中无需额外--enable即可生效。对包含问题代码的文件运行cppcheck test.cpp输出示例对应场景三的测试用例test.cpp:10:11: error: Using iterator that is a temporary. [danglingTemporaryLifetime] (void)m-first; ^若使用 XML 输出cppcheck --xml test.cpp同一报告还会携带cwe562与inconclusive属性便于 CI 系统按 CWE 归类处理。检查器 IDdanglingTemporaryLifetime也出现在 lib/settings.cpp 的检查器注册列表中并可通过--enablewarning --disable...等选项体系单独启用或禁用。CERT 与 MISRA 映射从 lib/checkersidmapping.cpp 可以看到该检查器被映射到 CERT 规则EXP54-CPPDo not access an object through a pointer to an object with a shorter lifetime与EXP61-CPPA lambda object shall not outlive any of its reference captured entities同时还出现在MISRA 18.6C 生命周期相关规则的映射中。也就是说danglingTemporaryLifetime报告可以直接作为这些安全标准合规性检查的结果来源。相关检查器同一家族的不同变体原文档将danglingTemporaryLifetime定位为悬垂生命周期检查家族的一员与其相邻的检查器各自的完整文档见 man/checkers/ 目录对比如下检查器报告消息悬垂对象形态文档路径danglingTemporaryLifetimeUsing pointer to dangling temporary.指向/迭代临时对象的指针、迭代器danglingTemporaryLifetime.mddanglingTempReferenceUsing reference to dangling temporary.绑定到临时对象的引用danglingTempReference.mdinvalidLifetimeUsing object that is out of scope.指向已离开作用域的具名局部变量的指针invalidLifetime.mdreturnTempReferenceReference to temporary returned.返回的引用指向临时对象danglingTempReference.md 中的关联链接returnTempReferencedanglingReferenceNon-local reference variable ... to local variable长生命周期引用绑定到局部变量danglingReference.md它们之间的区分要点在于悬垂来源danglingTemporaryLifetime的悬垂对象是临时对象无具名变量被保存的形式是指针或迭代器danglingTempReference针对同一类临时对象但保存形式是引用const/需区分生命周期延长是否生效invalidLifetime针对具名局部变量如if块内声明的变量被指针引用后在块外使用其文档invalidLifetime.md还给出了完整的修复前/修复后对照代码。选择修复方案时判断标准是一致的问自己这个值需要活多久——只需完整表达式内有效就在表达式内直接用需要跨表达式存活就按值保存而不是保存指针、迭代器或非延长性的引用。小结danglingTemporaryLifetime用一条清晰的规则捕捉了 C 中一类隐蔽而高危的缺陷临时对象在完整表达式结束时销毁而指向它的指针/迭代器却可能被顺带保存下来。通过 ValueFlow 生命周期分析、isDeadTemporary判定与 error path 溯源cppcheck 能在编译之外提前发现这些未定义行为。要彻底规避此类问题记住原文档的核心建议即可临时对象只在其创建表达式内通过指针/迭代器使用需要存活更久的数据务必按值存储。若想进一步研究其实现与边界情况建议对照阅读 lib/checkautovariables.cpp 的实现与 test/testautovariables.cpp 的完整测试用例。赞分享开发工具静态分析代码质量质量保障【免费下载链接】cppcheckstatic analysis of C/C code项目地址https://gitcode.com/gh_mirrors/cpp/cppcheck点击查看免费下载相关推荐cppcheck 检查器深度解析danglingTempReference 与 C 悬空临时对象引用的静态检测cppcheck 检查器深度解析danglingTempReference 与 C 悬空临时对象引用的静态检测 danglingTempReference开发工具静态分析代码质量质量保障littlefs 上手指南让 MCU 上的持久数据在断电后依然可靠littlefs 上手指南让 MCU 上的持久数据在断电后依然可靠 设备夜里被拔了电源重启后开机计数却归零配置文件也读不出来了——这类问题在单片机项目里很开发工具静态分析代码质量质量保障Cppcheck exceptDeallocThrow 检查器解析检测 delete 与异常抛出之间的悬垂指针use-after-freeCppcheck exceptDeallocThrow 检查器解析检测 delete 与异常抛出之间的悬垂指针use after free 本文深入解析开发工具静态分析代码质量质量保障上一篇Keyboard Chatter Blocker彻底解决机械键盘连击问题的终极免费方案下一篇Keyboard Chatter Blocker彻底解决机械键盘连击问题的终极免费方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑