来自C ++ / VS2010中sprintf的奇怪行为

时间:2012-01-12 21:34:07

标签: c++ visual-studio

我有一个超级简单的类,表示具有固定精度的十进制#,当我想格式化它时,我做了类似这样的事情:

assert(d.DENOMINATOR == 1000000);
char buf[100];
sprintf(buf, "%d.%06d", d._value / d.DENOMINATOR, d._value % d.DENOMINATOR);

令人惊讶的是(至少对我而言)这不起作用。即使d.DENOMINATOR没有均匀地划分d._value,%06d项也会全部出现0。如果我在格式字符串中添加额外的%d,我会看到正确的值显示在第三个位置 - 所以它就像是在我的两个之间秘密创建一个额外的参数。

如果我计算sprintf调用之外的两个术语,一切都表现出我的期望。我想用一个更简单的测试用例重现这个:

char testa[200];
char testb[200];
int x = 12345, y = 1000;
sprintf(testa, "%d.%03d", x/y, x%y);
int term1 = x/y, term2 = x%y;
sprintf(testb, "%d.%03d", term1, term2);

...但这可以正常使用。所以我完全不知道到底发生了什么,将来如何避免它等等。有谁可以为我解释这个问题?

(编辑:问题最终是因为d._value和d.DENOMINATOR都是长长的,所以%d还不够。非常感谢Serge的评论,下面指出了这个问题,马克的答案很快就提交了。 )

1 个答案:

答案 0 :(得分:3)

几乎可以肯定,您的术语组件是64位类型(在64位系统上可能是long),它将被传递到非类型安全的sprintf。因此,当您创建一个中间int时,大小是正确的,并且它可以正常工作。

g ++会用-Wall警告这个和许多其他有用的东西。当然,首选的解决方案是使用C ++ iostream进行格式化,因为它们完全是类型安全的。

备用解决方案是将表达式的结果转换为您告诉sprintf期望的类型,以便从内存中提取正确的字节数。

最后,当几乎每个编译器都支持sprintf时,永远不要使用snprintf来防止各种愚蠢的错误。你的代码现在很好,但是当有人稍后修改它并且它在缓冲区的末尾运行时,你可能需要花费数天来追踪腐败。