c++ - 使用 sprintf/printf 使用 %ld 格式字符串而不是 %d 和 int 数据类型的效果

标签 c++ printf coverity

我们有一些遗留代码,在某个时间点 long 数据类型被重构为 int 数据类型。在此重构期间,许多 printf/sprintf 格式语句被错误地保留为 %ld 而不是更改为 %d。例如:

int iExample = 32;
char buf[200];

sprintf(buf, "Example: %ld", iExample);

此代码在 GCC 和 VS2012 编译器上编译。我们使用 Coverity 进行静态代码分析,示例中的代码被标记为“Printf arg 类型不匹配”,严重程度为中等,CWE-686: Function Call With Incorrect Argument Type我可以看到,如果格式字符串是带有 unsigned int 类型的带符号 (%d) 的格式字符串或类似的内容,这肯定是个问题。

我知道 sprintf 等的“_s”版本更安全,并且上面的代码也可以重构为使用 std::stringstream 等。但是它是遗留代码...

我同意上面的代码确实应该至少使用 %d 或重构为使用类似 std::stringstream 的东西。

出于好奇,上面的代码是否会产生错误的结果?因为这个遗留代码已经存在了相当长的一段时间并且似乎工作正常。

已更新

  • 删除了 STL 一词的用法,并将其更改为 std::stringstream。

最佳答案

就标准而言,行为是未定义的,这意味着标准完全没有说明将发生什么。

在实践中,如果 intlong 具有相同的大小和表示形式,它很可能会“起作用”,即表现得好像正确的格式字符串已经用过的。 (intlong 在 32 位系统上通常都是 32 位)。

如果 longint 宽,它仍然可以“正确”工作。例如,调用约定可能是两种类型都在相同的寄存器中传递,或者两者都作为相同大小的机器“字”被压入堆栈。

或者它可能以任意糟糕的方式失败。如果 int 是 32 位且 long 是 64 位,则 printf 中尝试读取 long 对象的代码可能会得到一个 64 位对象,该对象由传递的实际 int 的 32 位和 32 位垃圾组成。或者额外的 32 位可能始终为零,但 32 位有效位位于 64 位对象的错误末端。还可以想象,在仅传递 32 位时获取 64 位可能会导致 其他 参数出现问题;您可能会得到 iExample 的正确值,但可能会从错误的堆栈偏移量中获取以下参数。

我的建议:应该修复代码以使用正确的格式字符串(并且你有工具来检测有问题的调用),但也做一些测试(在你关心的所有 C 实现上)看看它是否会导致实践中任何可见的症状。测试结果应该用于确定修复问题的优先级,而不是决定是否修复它们。如果代码现在明显失败,您应该立即修复它。如果没有,您可以等到稍后(大概您还有其他事情要做)。

关于c++ - 使用 sprintf/printf 使用 %ld 格式字符串而不是 %d 和 int 数据类型的效果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29171259/

相关文章:

shell - 使用 shell 将字节插入文件

创建 printf 的自定义版本

c++ - 使用 std::ifstream 进行输入验证

linux - 有没有静态代码分析器可以捕获此内存泄漏?

android - Coverity - 在 contentResolver.delete() 中显式取消引用空值 (FORWARD_NULL)

C++ 指针指向这段代码中的指针解释

c++ - 如何使这个 boost::enable_if 代码编译(SFINAE)?

android - 适用于 Android C/C++ 的 Eclipse CDT

c - sprintf在输出float和double变量时最多输出多少个字符?

c++ - cocos2d-X调用构造函数时调用retain