Search code examples
cargumentsargs

Length of each string argument


Recently I've started to get a little bit into the stdarg.h functions, because I want to have something similar to printf but instead of writing to the console I want to return it as a string.

Here's what I've come up with so far:

char *write(const char *format, ...)
{
    // init
    va_list arg;
    char *string;

    va_start (arg, format);
    vsprintf (string, format, arg);

    // done
    va_end (arg);
    return string;
}

Now the problem is, that string has not reserved memory and that's where I need help with a way to fix this function as I have not found any solution yet.

Thanks in advance


Solution

  • Use snprintf(NULL, 0 to check how long buffer you need. Then allocate memory. Then print to the string.

    char *my_write(const char *format, ...) {
        va_list va;
        va_start(va, format);
    
        // remember to have a separate va_list for each v*print function
        va_list va2;
        va_copy(va2, va);
        const int len = vsnprintf(NULL, 0, format, va2);
        va_end(va2);
    
        char *string = malloc((len + 1) * sizeof(*string));
        if (string != NULL) {
           vsprintf(string, format, va);
        }
        va_end(va);
    
        return string;
    }
    

    As suggested by @IanAbbott in comments you can invoke va_start twice, which seems to nicely simplify the code:

    char *my_write(const char *format, ...) {
        va_list va;
    
        va_start(va, format);
        const int len = vsnprintf(NULL, 0, format, va);
        va_end(va);
    
        char *string = malloc((len + 1) * sizeof(*string));
        if (string == NULL) {
           return NULL;
        }
    
        va_start(va, format);
        vsprintf(string, format, va);
        va_end(va);
    
        return string;
    }
    

    On platforms with with glibc you can also use vasprintf. Note that the name write is already used by posix write() function, I suggest to use a different name. With vasprintf GNU extension it becomes just:

    #define _GNU_SOURCE
    #include <stdio.h>
    char *write2(const char *format, ...) {
        va_list va;
        va_start(va, format);
        char *string;
        const int err = vasprintf(&string, format, va);
        va_end(va);
        if (err == -1) {
              return NULL;
        }
        return string;
    }