How can I tell how much destination buffer space ...


How can I tell how much destination buffer space I'll need for an arbitrary sprintf call? How can I avoid overflowing the destination buffer with sprintf?

✍: Guest


When the format string being used with sprintf is known and relatively simple, you can sometimes predict a buffer size in an ad-hoc way. If the format consists of one or two %s's, you can count the fixed characters in the format string yourself (or let sizeof count them for you) and add in the result of calling strlen on the string(s) to be inserted. For example, to compute the buffer size that the call
sprintf(buf, "You typed "%s"", answer);
would need, you could write:

int bufsize = 13 + strlen(answer);
int bufsize = sizeof("You typed "%s"") + strlen(answer);

followed by
char *buf = malloc(bufsize);
if(buf != NULL)
sprintf(buf, "You typed "%s"", answer);

You can conservatively estimate the size that %d will expand to with code like:
#include <limits.h> char buf[(sizeof(int) * CHAR_BIT + 2) / 3 + 1 + 1];
sprintf(buf, "%d", n);

This code computes the number of characters required for a base-8 representation of a number; a base-10 expansion is guaranteed to take as much room or less. (The +2 takes care of truncation if the size is not a multiple of 3, and the +1+1 leaves room for a leading - and a trailing \0.) An analogous technique could of course be used for long int, and the same buffer can obviously be used with %u, %o, and %x formats as well.
When the format string is more complicated, or is not even known until run time, predicting the buffer size becomes as difficult as reimplementing sprintf, and correspondingly error-prone (and inadvisable). A last-ditch technique which is sometimes suggested is to use fprintf to print the same text to a temporary file, and then to look at fprintf's return value or the size of the file (Using a temporary file for this application is admittedly clumsy and inelegant,but it's the only portable solution besides writing an entire sprintf format interpreter. If your system provides one, you can use a null or ``bit bucket'' device such as /dev/null or NUL instead of a temporary file.)
If there's any chance that the buffer might not be big enough, you won't want to call sprintf without some guarantee that the buffer will not overflow and overwrite some other part of memory. If the format string is known, you can limit %s expansion by using %.Ns for some N, or %.*sTo avoid the overflow problem, you can use a length-limited version of sprintf, namely snprintf. It is used like this:
snprintf(buf, bufsize, "You typed "%s"", answer);
snprintf has been available in several stdio libraries (including GNU and 4.4bsd) for several years. It has finally been standardized in C99.
As an extra, added bonus, the C99 snprintf provides a way to predict the size required for an arbitrary sprintf call. C99's snprintf returns the number of characters it would have placed in the buffer if there were room, not just how many it did place. Furthermore, it may be called with a null pointer and a buffer size of 0 and a null pointer as the destination buffer. Therefore, the call
nch = snprintf(NULL, 0, fmtstring, /* other arguments */ );
computes the number of characters required for the fully-formatted string. With that number (nch) in hand, you can then malloc a big-enough buffer and make a second snprintf call to fill it.
Yet another option is the (nonstandard) asprintf function, present in various C libraries including bsd's and GNU's, which formats to (and returns a pointer to) a malloc'ed buffer, like this:
char *buf;
asprintf(&buf, "%d = %s", 42, "forty-two");
/* now buf points to malloc'ed space containing formatted string */

2015-10-19, 774👍, 0💬