Determining sprintf buffer size - what's the standard?

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.


It's possible to make Daniel Standage's solution work for any number of arguments by using vsnprintf 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, 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.


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.

Tags:

C

Int

Printf