我们有一些遗留代码,在某个时间点 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。
最佳答案
就标准而言,行为是未定义的,这意味着标准完全没有说明将发生什么。
在实践中,如果 int
和 long
具有相同的大小和表示形式,它很可能会“起作用”,即表现得好像正确的格式字符串已经用过的。 (int
和 long
在 32 位系统上通常都是 32 位)。
如果 long
比 int
宽,它仍然可以“正确”工作。例如,调用约定可能是两种类型都在相同的寄存器中传递,或者两者都作为相同大小的机器“字”被压入堆栈。
或者它可能以任意糟糕的方式失败。如果 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/