C语言 确定 sprintf 缓冲区大小 - 标准是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3919995/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 06:41:23  来源:igfitidea点击:

Determining sprintf buffer size - what's the standard?

cintprintf

提问by Dominic Bou-Samra

When converting an int like so:

像这样转换 int 时:

char a[256];
sprintf(a, "%d", 132);

what's the best way to determine how large ashould 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?

确定a应该有多大的最佳方法是什么?我认为手动设置它很好(正如我所见,它无处不在),但它应该有多大?在 32 位系统上可能的最大 int 值是多少,是否有一些棘手的方法可以即时确定它?

采纳答案by Steve Jessop

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 arbitrary intis (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.

int 中的最大可能位数是CHAR_BIT * sizeof(int),十进制数字“值”至少 3 位,因此任意所需空间的宽松上限int(CHAR_BIT * sizeof(int) / 3) + 3. +3 是因为我们在除法时四舍五入,一个是符号,一个是 nul 终止符。

If by "on a 32 bit system" you mean that you know intis 32 bits, then you need 12 bytes. 10 for the digits, one for the sign, one for the nul terminator.

如果“在 32 位系统上”表示您知道int是 32 位,那么您需要 12 个字节。10 个数字,1 个符号,1 个 nul 终止符。

In your specific case, where the int to be converted is 132, you need 4 bytes. Badum, tish.

在您的特定情况下,要转换的 int 是132,您需要 4 个字节。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 bit int). But for difficult cases, in C99 you could just call snprintfto get the size, then mallocthat much. That's overkill for such a simple case as this.

如果固定大小的缓冲区可以在合理的范围内使用,它们是更简单的选择。我不那么谦虚地认为上面的界限是合理的(32 位是 13 字节而不是 12int字节,64 位是 23 字节而不是 21 字节int)。但是对于困难的情况,在 C99 中,您只需调用snprintf即可获取大小,然后就malloc这么多。对于这样一个简单的案例来说,这太过分了。

回答by Daniel Standage

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.

这里有些人认为这种方法是矫枉过正的,对于将整数转换为字符串,我可能更倾向于同意。但是当找不到合理的字符串大小界限时,我已经看到使用这种方法并且我自己也使用过它。

int size = snprintf(NULL, 0, "%d", 132);
char * a = malloc(size + 1);
sprintf(a, "%d", 132);

I'll break down what's going on here.

我会分解这里发生的事情。

  1. On the first line, we want to determine how many characters we need. The first 2 arguments to snprintftell it that I want to write 0 characters of the result to NULL. When we do this, snprintfwon't actually write any characters anywhere, it will simply return the number of characters that would have been written. This is what we wanted.
  2. On the second line, we are dynamically allocating memory to a charpointer. Make sure and add 1 to the required size (for the trailing \0terminating character).
  3. Now that there is enough memory allocated to the charpointer, we can safely use sprintfto write the integer to the charpointer.
  1. 在第一行,我们要确定我们需要多少个字符。前 2 个参数snprintf告诉它我想将结果的 0 个字符写入NULL. 当我们这样做时,snprintf实际上不会在任何地方写入任何字符,它只会返回本来应该写入的字符数。这就是我们想要的。
  2. 在第二行,我们动态地为一个char指针分配内存。确保将 1 添加到所需的大小(对于尾随\0终止字符)。
  3. 现在有足够的内存分配给char指针,我们可以安全地使用sprintf将整数写入char指针。

Of course you can make it more concise if you want.

当然,如果你愿意,你可以让它更简洁。

char * a = malloc(snprintf(NULL, 0, "%d", 132) + 1);
sprintf(a, "%d", 132);

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 huge charpointers 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.

除非这是一个“又快又脏”的程序,否则你总是想确保释放你调用的内存malloc。这就是动态方法在 C 中变得复杂的地方。但是,恕我直言,如果您不想char在大多数时间只使用其中的一小部分时分配巨大的指针,那么我不认为这是不好的做法。

回答by Regis Portalez

It's possible to make Daniel Standage's solution work for any number of arguments by using vsnprintfwhich is in C++11/C99.

通过使用C++11/C99 中的vsnprintf,可以使 Daniel Standage 的解决方案适用于任意数量的参数。

int bufferSize(const char* format, ...) {
    va_list args;
    va_start(args, format);
    int result = vsnprintf(NULL, 0, format, args);
    va_end(args);
    return result + 1; // safe byte for 
char *text;
text = malloc(val ? (int)log10((double)abs(val)) + (val < 0) + 2 : 2);
}

As specified in c99 standard, section 7.19.6.12 :

c99 标准第 7.19.6.12 节所述:

The vsnprintf function returns the number of characters that would have been written had n been sufficiently large, not counting the terminating null character, or a negative value if an encoding error occurred.

vsnprintf 函数返回在 n 足够大的情况下将被写入的字符数,不计算终止空字符,或者如果发生编码错误则返回负值。

回答by HopeItHelps

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:

我看到这个对话已经有几年了,但是我在尝试为 MS VC++ 寻找答案时发现了它,其中 snprintf 不能用于查找大小。我会发布我最终找到的答案,以防它对其他人有帮助:

VC++ has the function _scprintfspecifically to find the number of characters needed.

VC++有_scprintf专门查找所需字符数的功能。

回答by Pegasus Epsilon

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:

如果您正在打印一个简单的整数,而不是其他任何东西,那么确定输出缓冲区大小的方法要简单得多。至少在计算上更简单,代码有点钝:

snprintf( a, 256, "%d", 132 );

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 twiceto get the job done. Plus it uses less memory, generally, than the INT_MAX will require.

log10(value) 返回以 10 为基数存储非零正值所需的位数(减去 1)。对于小于 1 的数字,它有点偏离轨道,因此我们指定 abs(),并为零编码特殊逻辑(三元运算符,test ? truecase : falsecase)。添加一个用于存储负数符号的空间(val < 0),一个用于弥补与 log10 的差异,另一个用于空终止符(总计为 2),您刚刚计算给定数字所需的确切存储空间量,无需两次调用 snprintf() 或等效项即可完成工作。此外,它使用的内存通常少于 INT_MAX 所需的内存。

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.

但是,如果您需要非常快速地打印大量数字,请不要分配 INT_MAX 缓冲区,然后重复打印到该缓冲区。内存抖动越少越好。

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.

另请注意,您实际上可能不需要 (double) 而不是 (float)。我没有费心检查。像这样来回投射也可能是一个问题。YMMV 关于这一切。不过对我来说效果很好。

回答by Arun

Its good that you are worried about buffer size. To apply that thought in code, I would use snprintf

您担心缓冲区大小很好。为了在代码中应用这个想法,我会使用snprintf

snprintf( a, sizeof( a ), "%d", 132 );  // when a is array, not pointer

or

或者

##代码##

回答by EboMike

First off, sprintf is the devil. If anything, use snprintf, or else you risk trashing memory and crashing your app.

首先,sprintf 是魔鬼。如果有的话,请使用 snprintf,否则您可能会浪费内存并使您的应用程序崩溃。

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.

它也不依赖于您正在运行的系统。如果您在堆栈上定义缓冲区(如您的示例中所示),则取决于堆栈的大小。如果您自己创建了线程,那么您自己确定了堆栈大小,因此您知道限制。如果您期望递归或深度堆栈跟踪,那么您也需要格外小心。