我有一个超级简单的类,表示具有固定精度的十进制#,当我想格式化它时,我做了类似这样的事情:
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的评论,下面指出了这个问题,马克的答案很快就提交了。 )
答案 0 :(得分:3)
几乎可以肯定,您的术语组件是64位类型(在64位系统上可能是long
),它将被传递到非类型安全的sprintf
。因此,当您创建一个中间int时,大小是正确的,并且它可以正常工作。
g ++会用-Wall
警告这个和许多其他有用的东西。当然,首选的解决方案是使用C ++ iostream进行格式化,因为它们完全是类型安全的。
备用解决方案是将表达式的结果转换为您告诉sprintf
期望的类型,以便从内存中提取正确的字节数。
最后,当几乎每个编译器都支持sprintf
时,永远不要使用snprintf
来防止各种愚蠢的错误。你的代码现在很好,但是当有人稍后修改它并且它在缓冲区的末尾运行时,你可能需要花费数天来追踪腐败。