Determining sprintf buffer size - what's the standard?

CIntPrintf

C Problem Overview


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?

C Solutions


Solution 1 - C

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 snprintf tell it that I want to write 0 characters of the result to NULL. 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.
  2. On the second line, we are dynamically allocating memory to a char pointer. Make sure and add 1 to the required size (for the trailing \0 terminating character).
  3. Now that there is enough memory allocated to the char pointer, we can safely use sprintf to write the integer to the char pointer.

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 char 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.

Solution 2 - C

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

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 \0
}

As specified in [c99 standard][2], section 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.

[1]: http://www.cplusplus.com/reference/cstdio/vsnprintf/ "vsnprintf" [2]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

Solution 3 - C

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 int 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 bit int). But for difficult cases, in C99 you could just call snprintf to get the size, then malloc that much. That's overkill for such a simple case as this.

Solution 4 - C

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.

Solution 5 - C

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:

char *text;
text = malloc(val ? (int)log10((double)abs(val)) + (val < 0) + 2 : 2);

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.

Solution 6 - C

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.

Solution 7 - C

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

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

or

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDominic Bou-SamraView Question on Stackoverflow
Solution 1 - CDaniel StandageView Answer on Stackoverflow
Solution 2 - CRegis PortalezView Answer on Stackoverflow
Solution 3 - CSteve JessopView Answer on Stackoverflow
Solution 4 - CHopeItHelpsView Answer on Stackoverflow
Solution 5 - CPegasus EpsilonView Answer on Stackoverflow
Solution 6 - CEboMikeView Answer on Stackoverflow
Solution 7 - CArunView Answer on Stackoverflow