确定 sprintf 缓冲区大小 - 标准是什么?
当像这样转换 int 时:
char a[256];
sprintf(a, "%d", 132);
确定 a 应该有多大的最佳方法是什么?我认为手动设置它是可以的(因为我已经看到它到处使用),但它应该有多大? 32 位系统上可能的最大 int 值是多少,是否有一些棘手的方法可以动态确定该值?
When converting an int like so:
char a[256];
sprintf(a, "%d", 132);
what's the best way to determine how large a should be? I assume manually setting it is fine (as I've seen it used everywhere), but how large should it be? What's the largest int value possible on a 32 bit system, and is there some tricky way of determining that on the fly?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
这里有些人认为这种方法太过分了,对于将整数转换为字符串,我可能更倾向于同意。但是,当无法找到字符串大小的合理界限时,我看到了这种方法的使用,并且自己也使用过。
我将分解这里发生的事情。
snprintf
的前 2 个参数告诉它我想将结果的 0 个字符写入NULL
。当我们这样做时,snprintf
实际上不会在任何地方写入任何字符,它只会返回已写入的字符数。这就是我们想要的。char
指针。确保将 1 添加到所需的大小(对于尾随\0
终止字符)。char
指针,我们可以安全地使用sprintf
将整数写入char
指针。当然,如果你愿意的话,你可以让它更简洁。
除非这是一个“快速而肮脏”的程序,否则您总是希望确保释放使用
malloc
调用的内存。这就是 C 的动态方法变得复杂的地方。但是,恕我直言,如果您不想分配巨大的char
指针,而大多数时候您只使用其中的一小部分,那么我认为这不是一个坏方法。Some here are arguing that this approach is overkill, and for converting ints to strings I might be more inclined to agree. But when a reasonable bound for string size cannot be found, I have seen this approach used and have used it myself.
I'll break down what's going on here.
snprintf
tell it that I want to write 0 characters of the result toNULL
. When we do this,snprintf
won't actually write any characters anywhere, it will simply return the number of characters that would have been written. This is what we wanted.char
pointer. Make sure and add 1 to the required size (for the trailing\0
terminating character).char
pointer, we can safely usesprintf
to write the integer to thechar
pointer.Of course you can make it more concise if you want.
Unless this is a "quick and dirty" program, you always want to make sure to free the memory you called with
malloc
. This is where the dynamic approach gets complicated with C. However, IMHO, if you don't want to be allocating hugechar
pointers when most of the time you will only be using a very small portion of them, then I don't think this is bad approach.通过使用 vsnprintf< 可以使 Daniel Standage 的解决方案适用于任意数量的参数/a> 是 C++11/C99 中的。
如 c99 标准第 7.19 节中所述。 6.12:
It's possible to make Daniel Standage's solution work for any number of arguments by using vsnprintf which is in C++11/C99.
As specified in c99 standard, section 7.19.6.12 :
int 中最大可能的位数是 CHAR_BIT * sizeof(int) ,十进制数字“值”至少 3 位,因此任意 < 所需的空间上限是宽松的代码>int 是
(CHAR_BIT * sizeof(int) / 3) + 3
。 +3 是因为我们在除法时向下舍入这一事实,一个用于符号,一个用于 nul 终止符。如果“在 32 位系统上”意味着您知道
int
是 32 位,那么您需要 12 个字节。 10 个数字,1 个符号,1 个 nul 终止符。在您的具体情况下,要转换的 int 为
132
,您需要 4 个字节。巴杜姆,蒂什。如果可以在合理的范围内使用固定大小的缓冲区,则它们是更简单的选择。我毫不谦虚地认为上面的界限是合理的(对于 32 位
int
来说是 13 个字节而不是 12 个字节,对于 64 位int
来说是 23 个字节而不是 21 个字节)。但对于困难的情况,在 C99 中,您只需调用snprintf
来获取大小,然后调用malloc
即可。对于这样一个简单的案例来说,这太过分了。The max possible number of bits in an int is
CHAR_BIT * sizeof(int)
, and a decimal digit is "worth" at least 3 bits, so a loose upper bound on the space required for an arbitraryint
is(CHAR_BIT * sizeof(int) / 3) + 3
. That +3 is one for the fact that we rounded down when dividing, one for the sign, one for the nul terminator.If by "on a 32 bit system" you mean that you know
int
is 32 bits, then you need 12 bytes. 10 for the digits, one for the sign, one for the nul terminator.In your specific case, where the int to be converted is
132
, you need 4 bytes. Badum, tish.Where fixed-size buffers can be used with a reasonable bound, they are the simpler option. I not-so-humbly submit that the bound above is reasonable (13 bytes instead of 12 for 32 bit
int
, and 23 bytes instead of 21 for 64 bitint
). But for difficult cases, in C99 you could just callsnprintf
to get the size, thenmalloc
that much. That's overkill for such a simple case as this.我看到这个对话已经有几年了,但我在尝试寻找 MS VC++ 的答案时发现了它,其中 snprintf 不能用于查找大小。我将发布我最终找到的答案,以防它对其他人有帮助:
VC++ 有函数
_scprintf
专门用于查找所需的字符数。I see this conversation is a couple of years old, but I found it while trying to find an answer for MS VC++ where snprintf cannot be used to find the size. I'll post the answer I finally found in case it helps anyone else:
VC++ has the function
_scprintf
specifically to find the number of characters needed.如果您要打印一个简单的整数,而没有其他任何内容,则有一种更简单的方法来确定输出缓冲区大小。至少在计算上更简单,代码有点迟钝:
log10(value) 返回以 10 为基数存储正非零值所需的位数(减一)。对于小于 1 的数字,它有点偏离轨道,所以我们指定abs(),并为零编写特殊逻辑(三元运算符,test ? truecase : falsecase)。为存储负数符号 (val < 0) 的空间添加 1,为弥补与 log10 的差值添加 1,为空终止符添加 1(总共为 2),这样您就完成了计算给定数字所需的确切存储空间量,而无需调用 snprintf() 或等效函数两次来完成工作。另外,它使用的内存通常比 INT_MAX 所需的内存要少。
但是,如果您需要非常快速地打印大量数字,请麻烦分配 INT_MAX 缓冲区,然后重复打印到该缓冲区。内存抖动越少越好。
另请注意,您实际上可能不需要 (double) 而不是 (float)。我没有费心去检查。像这样来回投射也可能是一个问题。 YMMV 关于这一切。不过对我来说效果很好。
If you're printing a simple integer, and nothing else, there's a much simpler way to determine output buffer size. At least computationally simpler, the code is a little obtuse:
log10(value) returns the number of digits (minus one) required to store a positive nonzero value in base 10. It goes a little off the rails for numbers less than one, so we specify abs(), and code special logic for zero (the ternary operator, test ? truecase : falsecase). Add one for the space to store the sign of a negative number (val < 0), one to make up the difference from log10, and another one for the null terminator (for a grand total of 2), and you've just calculated the exact amount of storage space required for a given number, without calling snprintf() or equivalents twice to get the job done. Plus it uses less memory, generally, than the INT_MAX will require.
If you need to print a lot of numbers very quickly, though, do bother allocating the INT_MAX buffer and then printing to that repeatedly instead. Less memory thrashing is better.
Also note that you may not actually need the (double) as opposed to a (float). I didn't bother checking. Casting back and forth like that may also be a problem. YMMV on all that. Works great for me, though.
首先,sprintf 是魔鬼。如果有的话,请使用 snprintf,否则您将面临内存浪费和应用程序崩溃的风险。
至于缓冲区大小,它与所有其他缓冲区一样 - 尽可能小,根据需要尽可能大。就您而言,您有一个有符号整数,因此请采用尽可能大的大小,并随意添加一点安全填充。没有“标准尺寸”。
它也不依赖于您运行的系统。如果您在堆栈上定义缓冲区(如您的示例中所示),则它取决于堆栈的大小。如果您自己创建了线程,那么您自己确定了堆栈大小,因此您知道限制。如果您期望递归或深层堆栈跟踪,那么您也需要格外小心。
First off, sprintf is the devil. If anything, use snprintf, or else you risk trashing memory and crashing your app.
As for the buffer size, it's like all other buffers - as small as possible, as big as necessary. In your case, you have a signed integer, so take the largest possible size, and feel free to add a little bit of safety padding. There is no "standard size".
It's also not dependent on what system you're running on. If you define the buffer on the stack (like in your example), it depends on the size of the stack. If you created the thread yourself, then you determined the stack size yourself, so you know the limits. If you are going to expect recursion or a deep stack trace, then you need to extra careful as well.
您担心缓冲区大小是件好事。要在代码中应用该思想,我将使用 snprintf
或
Its good that you are worried about buffer size. To apply that thought in code, I would use snprintf
or