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

时间:2015-03-20 16:20:17

标签: c++ printf coverity

我们有一些遗留代码,在某个时间点,长数据类型被重构为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)或者这些行中的某些内容,那肯定会出现问题。

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

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

出于好奇,是否有上述代码会产生错误结果的情况?由于此遗留代码已存在很长时间,并且似乎正常工作。

已更新

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

2 个答案:

答案 0 :(得分:4)

就标准而言,行为是未定义的,这意味着标准对所发生的事情一无所知。

实际上,如果intlong具有相同的大小和表示,它很可能“有效”,即表现得好像使用了正确的格式字符串。 (intlong在32位系统上通常为32位。

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

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

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

答案 1 :(得分:1)

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