3

我们有一些遗留代码在某个时间点将 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 我可以看到这绝对是一个问题格式字符串是带有无符号 int 类型或类似这些行的有符号 (%d) 的格式字符串。

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

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

出于好奇,是否存在上述代码会产生错误结果的情况?由于这个遗留代码已经存在了很长一段时间,并且似乎运行良好。

更新

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

2 回答 2

4

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

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

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

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

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

于 2015-03-20T19:02:12.733 回答
1

它是未定义的,取决于实现。在 int 和 long 具有相同大小的实现中,它可能会按预期工作。但是只需在任何具有 32 位 int 和 64 位 long 的系统上尝试它,特别是如果您的整数不是最后一个格式参数,并且您可能会遇到 printf 读取 64 位而仅提供 32 位的问题,其余的很可能是垃圾,并且可能,根据对齐方式,以下参数也无法正确访问。

于 2015-03-20T17:09:17.940 回答