cppcheck invalidPrintfArgType_n 检查器深入解析:printf `%n` 实参必须是可写指针
开发工具静态分析代码质量质量保障【免费下载链接】cppcheckstatic analysis of C/C code项目地址https://gitcode.com/gh_mirrors/cpp/cppcheck点击查看免费下载本篇文章围绕 cppcheck 静态分析器中的invalidPrintfArgType_n检查器展开它专门用于发现printf族调用中%n格式说明符与实参类型不匹配的问题。读完本文你将掌握%n未定义行为的成因、该检查器在 lib/checkio.cpp 中的判定逻辑、完整修复示例以及如何借助仓库内测试用例验证其行为。检查器速览invalidPrintfArgType_n是 cppcheck 的 I/O 检查族CheckIO实现于 lib/checkio.cpp中针对%n格式说明符的类型检查器官方文档位于 man/checkers/invalidPrintfArgType_n.md。其元信息如下属性值错误标识Error IDinvalidPrintfArgType_n消息模板%n in format string (no. N) requires int * but the argument type is X.类别CategoryUndefined Behaviour未定义行为严重级别SeverityWarning / Portability适用语言C / C关联 CWECWE-686函数调用参数类型错误消息中的no. N表示该实参相对于格式字符串的第 N 个可变参数位置从 1 开始X为实际推导出的实参类型。从 lib/checkio.cpp 可以看到该告警以Certainty::normal正常置信度上报无需--inconclusive即可触发同时告警级别受getSeverity()控制详见下文严重级别切换一节。它检测什么%n与可写指针printf家族中%n是一个特殊的存在它不像%s、%d那样从参数读取数据而是将到目前为止已输出的字符数写入实参所指向的内存。因此标准要求%n的实参必须是一个指向整数的可写指针int *。该检查器的职责是当%n收到的实参明显不是可写指针时报警。官方文档明确了两类被检出的情形普通整数如int、long等非指针值const限定的指针不可写典型如字符串字面量其类型为const char *。值得强调的是文档指出该检查器只验证实参是某个可写指针而并不严格校验它必须是int *——也就是说void *、char *等其它非const指针不会被本检查器报错这是其与类型完全精确匹配类检查的差异点。源码中的判定条件在 lib/checkio.cpp 的checkFormatString()逐格式符解析逻辑中case n分支的触发条件如下case n: if ((argInfo.isKnownType() (!argInfo.isArrayOrPointer() || argInfo.typeToken-strAt(-1) const)) || argListTok-tokType() Token::eString) invalidPrintfArgTypeError_n(tok, numFormat, argInfo); done true; break;该条件可以拆解为三条触发路径实参是字符串字面量argListTok-tokType() Token::eString即传入...或宽字符串字面量类型为const char */const wchar_t *不可写时必然报警实参是已知类型的非指针/非数组值isKnownType() !isArrayOrPointer()即明确的标量如int、long、std::string对象等直接报警实参指针带有const修饰typeToken-strAt(-1) const即类型令牌前紧邻const的声明属于不可写指针路径。而一个普通的int *指针实参满足isArrayOrPointer()且不带const前缀不会触发本检查器从而避免对正确代码产生误报。为什么这是未定义行为Motivation%n会把当前已打印的字符个数写回实参指针所指向的地址。当传入的不是指针而是普通整数时printf会把这个整数值当作地址去写入——这等同于向一个随机内存地址执行写操作属于标准意义上的未定义行为可能造成直接崩溃访问了无效/受保护的内存页内存破坏恰好命中某个可写地址静默篡改数据安全风险正因%n可向任意地址写入历史上被广泛用于格式化字符串攻击%n配合精心构造的格式串可改写返回地址等因此文档明确指出部分平台与库实现出于安全考虑会直接禁用%n的写入。依赖%n的代码在这些平台上会得到完全不同甚至被忽略的行为这也是其严重级别被标注为含 Portability可移植性的原因。与%s要求char *读取字符串和%p要求指针读取地址值不同%n是写操作所以实参错误造成的后果通常更严重——读取错误类型最多产生垃圾值而写入错误地址则直接破坏内存。这组同族检查器分别由invalidPrintfArgType_s与invalidPrintfArgType_p负责实现思路一致。如何修复How to fix官方文档给出了最典型的错误写法与修复方式修复前错误#include cstdio void f(int x) { printf(%n, x); // - 需要一个 int *而不是 int }修复后正确#include cstdio void f(int *x) { printf(%n, x); }更多修复场景结合仓库测试用例见下文实际工程中常见的错误与正确形态还包括字符串字面量误用printf(%n, s4);会以const char *类型被报告应改为传入真正的可写指针宽字符串字面量误用printf(%n, Ls5W);会以const wchar_t *类型被报告类对象误用printf(%n, s);std::string对象会被报告为std::string类型非指针整数值int i; printf(%n, i);会以signed int类型被报告必须改为printf(%n, i);别名类型typedef实参当实参类型是size_t、ptrdiff_t等 typedef 别名时告警级别会从 Warning 提升为 Portability见下节因为这类代码在不同平台上表现不一致的风险更高。正确使用%n的典型场景是统计已输出的字符数例如#include cstdio void f() { int n 0; printf(hello%n, n); // 将已输出的字符数5写入 n }若你的真实意图是打印某个指针的地址请使用%p而非%n若意图是输出整数请使用%d/%u等整数格式符并参考同族的 invalidPrintfArgType_sint 与 invalidPrintfArgType_uint 检查器。源码实现报错路径与严重级别切换消息生成当检测命中时调用链为checkFormatString()→invalidPrintfArgTypeError_n()声明于 lib/checkio.h实现于 lib/checkio.cppvoid CheckIOImpl::invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) { const Severity severity getSeverity(argInfo); if (!mSettings.severity.isEnabled(severity)) return; std::ostringstream errmsg; errmsg %n in format string (no. numFormat ) requires \int *\ but the argument type is ; argumentType(errmsg, argInfo); errmsg .; reportError(tok, severity, invalidPrintfArgType_n, errmsg.str(), CWE686, Certainty::normal); }这段实现同时验证了官方文档中的多个细节消息模板中的int *期望类型、no. N参数位置编号以及错误标识invalidPrintfArgType_n与 CWE-686 的绑定。严重级别切换getSeverity() 决定了本检查器的告警最终以warning还是portability呈现Severity CheckIOImpl::getSeverity(const ArgumentInfo *argInfo) { return (argInfo argInfo-typeToken !argInfo-typeToken-originalName().empty()) ? Severity::portability : Severity::warning; }当实参类型带有originalName即通过typedef引入的别名类型如size_t、intptr_t等时按Portability上报普通内建类型按Warning上报。这与官方文档标明的 Warning/Portability 双重级别完全对应也解释了为什么这类告警既提示未定义行为风险、又提示跨平台可移植性风险。若对应级别被--enable/--disable或--suppress关闭则invalidPrintfArgTypeError_n会在上报前直接返回。测试用例验证仓库的单元测试 test/testio.cpp 对该检查器行为有直接验证。printf侧的核心用例test/testio.cpp构造了多种实参形态并断言了精确的告警输出void foo(const int* cpi, const int ci, int i, int* pi, std::string s) { printf(%n, cpi); // 不报const int * 仍被视为指针 printf(%n, ci); // 报signed int printf(%n, i); // 报signed int printf(%n, pi); // 不报int * 正确 printf(%n, s); // 报std::string printf(%n, s4); // 报const char * }期望输出恰好覆盖了本文前述的全部触发路径[test.cpp:3]: (warning) %n in format string (no. 1) requires int * but the argument type is signed int. [test.cpp:4]: (warning) %n in format string (no. 1) requires int * but the argument type is signed int. [test.cpp:6]: (warning) %n in format string (no. 1) requires int * but the argument type is std::string. [test.cpp:7]: (warning) %n in format string (no. 1) requires int * but the argument type is const char *.紧随其后的用例还验证了宽字符串字面量场景test/testio.cppprintf(%n, Ls5W); // 报(warning) %n ... requires int * but the argument type is const wchar_t *.此外scanf侧的%n由invalidScanfArgType_int家族负责test/testio.cpp 有大量TEST_SCANF_WARN/TEST_SCANF_NOWARN用例可见 cppcheck 对读写两侧的%n均有覆盖。相关检查器invalidPrintfArgType_s同一思路用于%s要求char *invalidPrintfArgType_p同一思路用于%p要求地址/指针invalidPrintfArgType_sint / invalidPrintfArgType_uint%d/%u等整数格式符的符号/无符号类型匹配检查invalidScanfArgType_s / invalidScanfArgType_intscanf侧的同类检查。小结invalidPrintfArgType_n是 cppcheck I/O 检查族中针对%n写操作特性的专项检查器。它从实参必须是可写指针这一语义出发将普通整数、const限定指针含字符串字面量等明显非法实参识别为未定义行为并以 Warning/Portability 双重级别、绑定 CWE-686 上报其判定条件、消息生成与严重级别切换逻辑均可直接在 lib/checkio.cpp 中溯源行为正确性由 test/testio.cpp 中的多组用例锁定。修复时只需将实参改为真正的非const指针如i并在需要打印地址时改用%p。赞分享开发工具静态分析代码质量质量保障【免费下载链接】cppcheckstatic analysis of C/C code项目地址https://gitcode.com/gh_mirrors/cpp/cppcheck点击查看免费下载相关推荐Cppcheck 检查器详解arithOperationsOnVoidPointer 与 void* 指针算术的可移植性隐患Cppcheck 检查器详解arithOperationsOnVoidPointer 与 void 指针算术的可移植性隐患 导读 arithOperation开发工具静态分析代码质量质量保障cppcheck 检查器实战constVariablePointer 与指针 const 正确性const-correctnesscppcheck 检查器实战constVariablePointer 与指针 const 正确性const correctness 导读 本文围绕 cpp开发工具静态分析代码质量质量保障Cppcheck 指针与整数赋值移植性检查AssignmentAddressToInteger / AssignmentIntegerToAddress深度解析Cppcheck 指针与整数赋值移植性检查AssignmentAddressToInteger / AssignmentIntegerToAddress深度开发工具静态分析代码质量质量保障上一篇fish-shell 终端标题定制指南全面解析 fish_title 与 fish_tab_title下一篇Mem Reduct中文界面终极配置指南3种方法解决语言切换难题创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Spring Boot全栈实践:敦煌文化旅游管理系统的设计与实现

Spring Boot全栈实践:敦煌文化旅游管理系统的设计与实现

做毕设或者练手项目选管理系统的人很多,但真正能把一个系统做成"有的讲、拿得出手、面试能聊"的,其实不多。今天想拆解的这个敦煌文化旅游管理系统,就是典型的Spring Boot全栈项目——围绕敦煌这一个大IP,把旅游攻略、酒…

2026/10/4 14:53:41 阅读更多 →
基于springboot + vue人力资源管理系统(源码+数据库+文档)

基于springboot + vue人力资源管理系统(源码+数据库+文档)

人力资源管理系统 目录 基于springboot vue人力资源管理系统 一、前言 二、系统功能演示 三、技术选型 四、其他项目参考 五、代码参考 六、测试参考 七、最新计算机毕设选题推荐 八、源码获取: 基于springboot vue人力资源管理系统 一、前言 博主介绍&…

2026/10/4 14:52:41 阅读更多 →
第29章:RAGFlow Kubernetes/Helm 部署与弹性扩缩容

第29章:RAGFlow Kubernetes/Helm 部署与弹性扩缩容

1 项目背景 业务场景 「云帆科技」的 Docker Compose 单机部署已经支撑了 500 人三个月的使用。但随着业务量增长,单机的资源瓶颈逐渐显现——32GB 内存已被占满 85%,而且每逢周一早高峰(全员集中问答),CPU 飙到 95%…

2026/10/4 14:52:41 阅读更多 →

最新新闻

【claude】cli工具安装后,把 settings 改到 TaoToken 的完整配置指南

【claude】cli工具安装后,把 settings 改到 TaoToken 的完整配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 15:38:10 阅读更多 →
插件系统开发实战:plugin.json配置、TypeScript SDK与CLI加载机制详解

插件系统开发实战:plugin.json配置、TypeScript SDK与CLI加载机制详解

1. 从“plugins”这个词说起:它到底在解决什么问题“plugins”这个词,放在今天的开发语境里,几乎已经成了一个绕不开的基础设施级概念。不管你是用 Cursor 写代码、用 Codex CLI 跑命令、还是在 VS Code 里装扩展,背后都离不开插件…

2026/10/4 15:38:10 阅读更多 →
插件开发实战:从plugin.json到加载失败排查的完整指南

插件开发实战:从plugin.json到加载失败排查的完整指南

1. 从“plugins”这个词说起:它到底在解决什么问题“plugins”这个词,放在今天的开发语境里,几乎已经成了一个绕不开的基础设施级概念。不管你是用 Cursor 写代码、用 Codex CLI 跑命令、还是在 VS Code 里装扩展,背后都离不开插件…

2026/10/4 15:38:10 阅读更多 →
2026年AIGC自检工具怎么选?毕业之家 vs 7款主流平台实测对比(附免费攻略)

2026年AIGC自检工具怎么选?毕业之家 vs 7款主流平台实测对比(附免费攻略)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 15:38:10 阅读更多 →
插件机制深度解析:从IAR到MusicFree从报错到实战

插件机制深度解析:从IAR到MusicFree从报错到实战

最近在翻搜索记录的时候,发现 plugins 这个词的热度比我想象的高得多。有人一头雾水地问 iar plugins 是干什么的,有人对着 failed to load plugins web boot: 2 entries did not activate 这种报错发呆,还有人刚拿到新设备就急着折腾 musicf…

2026/10/4 15:38:10 阅读更多 →
vscode使用插件KoroFileHeader添加注释,以及解决快捷键冲突详解(fileheader、cursorTip)

vscode使用插件KoroFileHeader添加注释,以及解决快捷键冲突详解(fileheader、cursorTip)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 15:37:10 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 11:40:45 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 9:43:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →