Fun with the asprintf() Function

If you desire to store formatted output in a buffer, the snprintf() function, covered in last week’s Lesson, is a safe alternative to the standard Library sprintf() function. An even better choice is the asprintf() function.

The asprintf() function sends formatted output into a perfectly-sized buffer, allocated within the function itself. Here is its man page format:

int asprintf(char **ret, const char *format, ...);

The first argument is the scary pointer-pointer thing, but it’s really the address of a pointer. When the function returns, the pointer contains the formatted output string; the final two arguments are the same as for the standard printf() function: a format string and variable argument list.

Sample code:

#include <stdio.h>

int main()
{
    char *buffer;
    int r;

    r = asprintf(&buffer,"The total is %d\n",5+8);

    puts(buffer);
    printf("%d characters generated\n",r);

    return(0);
}

The char pointer buffer is declared at Line 5. Its address is passed to the asprintf() function at Line 8.

Within the asprintf() function, the buffer is allocated, the size properly set to be filled with the formatted output. No overflow is possible.

The buffer’s contents are output at Line 11. The asprintf() function’s return value, stored in variable r, is also output. This value is equal to the length of the string returned (the size of the buffer, minus one for the null character):

The total is 13

16 characters generated

Using this function is far safer than using the sprintf() function, which doesn’t check for buffer overflow. And it’s better than the snprintf() function, which has a set buffer size. With the asprintf() function, the buffer size can be anything; all you must do is supply a char pointer to reference the string created. Well, and get over any double-pointer fears you harbor. Speaking of which, here is the double-pointer version of the same code:

#include <stdio.h>

int main()
{
    char **buffer;
    int r;

    r = asprintf(buffer,"The total is %d\n",5+8);

    puts(*buffer);
    printf("%d characters generated\n",r);

    return(0);
}

The double-pointer buffer is declared at Line 5. It’s passed directly as the first argument of asprintf() at Line 8; no need to prefix it with an ampersand.

Line 10 uses the *buffer argument in the puts() function. The format *buffer refers to the string, whereas **buffer refers to the string’s address. Yes, it’s all complicated pointer nonsense, yet the code runs identically to the first example.

If you the -Wall switch to build this second example of the code, you may see an uninitialized pointer warning. Ignore it. The warning is legitimate but the fix, which is to initialize the pointer to NULL, may crash the program. It’s safe to pass the uninitialized **buffer pointer to the asprintf() function because the function itself initializes it.

The **buffer or &buffer format is required because a pointer must be passed indirectly to a function that modifies the pointer’s address. Otherwise the function loses the pointer’s address when it returns, which defeats the purpose. You can read my series of Lessons on this topic if it confuses you or you need to brush-up.

Leave a Reply