Let Me Tell you about a C function called asprintf()

Before I begin, thanks to Suraj Pal, for his comments that improved this post. Now onto the scenario.
Suppose you are working with a C/C++ function, passing around a string. This time you want to pass the variable by reference. This can be because:

  • The string is big and you are working in a resource constrained environment.
  • The calling function is gonna get destroyed immediately after the call, and you expect the string to stick around after the child function is done with its work.

The way you usually do this is to create a char pointer, allocate some memory and then copy over the source string to it. Then you can pass the pointer. The task of de-allocating the memory is given to the consumer function.

These steps can be encoded into

char* x = malloc(11 * sizeof(char));
sprintf(x,"%s", "Hello World");

The eagle eyed among you might have noticed a problem there. The allocated memory is 11 bytes, but the string, when you count the terminating null, is 12 bytes. The problem is, sprintf doesn't care and writes the last character out of the allocated memory.

This causes one of the most frustrating type of bug, an intermittent bug, One that happens sometimes, but otherwise stays hidden in silence. If you're lucky, it will trample on memory in-between allocated blocks, and will do no harm -- this time. It's good to use a tool like ElectricFence that causes overruns like these to fail-fast.

The way you use sprintf correctly is to measure the string length and then allocate a buffer, but that requires you to assign the string to a char array like so, and it won't work with more complex formatting that you usually do.

char buffer[100] = "Hello world";
char* x = malloc(strlen(buffer) * sizeof(char) + 1);
sprintf(x,"%s", buffer);

Note: The +1 in the end is to account for the ending null, as strlen doesn't do that for us.

Another solution is to use snprintf. This is just like sprintf but doesn't overflow the buffer if the formatted string length exceeds the given length. It truncates the string to the maximum length you supply.

char *x = malloc(6 * sizeof(char));
int size = snprintf(x, 6, "%s", "Hello world"); // writes "Hello" + null

The return value size is the length that would have been written if space was available -- not including the terminating null.

In this case, if size is greater than or equal to 5 then you know that truncation occurred - and if you didn't want truncation, you could allocate a new string and try snprintf() again.

char *x = malloc(BUF_LEN * sizeof(char));
int size = snprintf(x, 5, "%s%s%s", "12", "34", "56");
if (size >= BUF_LEN) {
    realloc(&x,(size + 1) * sizeof(char));
    snprintf(x, size + 1 , "%s%s%s", "12", "34", "56");
}

(That's a pretty naive algorithm, but it illustrates the point. There may yet be bugs in it, which further illustrates the point -- this stuff is easy to screw up.)

asprintf() does this in one step for you - calculates the length of the string, allocates that amount of memory, and writes the string into it.

char *x;
int size = asprintf(&x, "%s%s%s", "12", "34", "56");

In all cases, once you've finished with x you need to release it, or you leak memory:

free(x);

asprintf() is an implicit malloc(), so you have to check it worked, just as you would with malloc() or any other system call.

if (size == -1 ) {
   /* deal with error in some way */
}

Note that asprintf() is part of the GNU and BSD extensions to libc - you can't be sure it will be available in every C environment. sprintf() and snprintf() are part of the POSIX and C99 standards.

Source

Most of the text here is picked from the following answer from stackoverflow. I reposted this, as I found that asprintf was not common knowledge and this it took some time to get this exact answer in SO. Thanks to user @slim for the clear and concise explanation.

Related Posts

Leave a Reply

%d bloggers like this: