stdarg.h formating string via snprintf not working, arguments are ignored

In C (gcc, ubuntu 22.04) I’m trying to create formatted string using format string and … arguments, like a sprintf, but it would return the formatted string, e.g. char *s = myformat("Hello %s", name) but for some reason it is not working, as if the name was empty.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

char* myformat(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    size_t len = snprintf(NULL, 0, fmt, args);
    if (len > 0) {
        char *s = (char*)malloc(len + 1);
        if (s) {
            printf("len=%ld\n", len); // prints len=11 which is "hello  zzz"+1
            snprintf(s, len + 1, fmt, args);
        } else {
            fprintf(stderr, "error: create_formated_string() failed to allocate %ld chars\n", len + 1);
            exit(1);
        }
        return s;
    }
    va_end(args);
    return NULL;
}

int main(void) {
    char *name = "John";
    char *s = myformat("Hello %s zzz", name);
    printf("s=%s\n", s);
    free(s);
    return 0;
}                              

It will print

s=Hello zzz

Instead of

s=Hello John zzz

I’m compiling using: "gcc test.c" on Ubuntu 22.04.

I tried using vsnprintf but it now prints random characters instead of name:

char* myformat(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    size_t len = vsnprintf(NULL, 0, fmt, args);
    char *s;
    if (len > 0) {
        s = (char*)malloc(len + 1);
        if (s) {
            printf("len=%ld\n", len); // prints len=11 which is "hello  zzz"+1
            vsnprintf(s, len + 1, fmt, args);
        } else {
            fprintf(stderr, "error: create_formated_string() failed to allocate %ld chars\n", len + 1);
            exit(1);
        }
    }
    va_end(args);
    return s;
}

int main(void) {
    char *name = "John";
    char *s = myformat("Hello %s zzz", name);
    printf("s=%s\n", s);
    free(s);
    return 0;
}   

        

>Solution :

It does not work like this. You need to use special functions which take va_list argument.

int vsnprintf (char * s, size_t n, const char * format, va_list arg );

But it will not work without some modifications. The fist call will modify args and you need to copy it.

char* myformat(const char *fmt, ...) {
    va_list args,copy;
    va_start(args, fmt);
    va_copy(copy, args);
    size_t len = vsnprintf(NULL, 0, fmt, args);
    if (len > 0) {
        char *s = malloc(len + 1);
        if (s) {
            printf("len=%ld\n", len); // prints len=11 which is "hello  zzz"+1
            vsnprintf(s, len + 1, fmt, copy);
        } else {
            fprintf(stderr, "error: create_formated_string() failed to allocate %zu chars\n", len + 1);
            exit(1);
        }
        return s;
    }
    va_end(args);
    va_end(copy);
    return NULL;
}

https://godbolt.org/z/qqsz89dvz

Also %ld is not correct to print size_t parameter. You need to use %zu format

Leave a Reply