Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

stdarg and NULL arguments

I want a function that, when called with a varying number of arguments, return the first non-NULL one. I've tried this, but it core dumps on for loop:

char *first(char *args, ...)
{
    va_list ap;
    char *r = NULL, *p;

    va_start(ap, args);
    for (p = args; *p; p++) {
        r = va_arg(ap, char*);
        if (r != NULL) break;
    }
    va_end(ap);
    return r;
}

char *q = NULL;
char *w = NULL;
char *e = "zzz";
char *r = NULL;
printf("%s\n", first(q, w, e, r)); // ought to print "zzz"
like image 977
Alexander Gromnitsky Avatar asked Dec 21 '22 08:12

Alexander Gromnitsky


1 Answers

args is not an array of arguments. It's just the first argument you passed to first. So its value in this case is the value of q.

You can't iterate over va args like you are doing.

Do this:

va_start(ap, args);
do {
    r = va_arg(ap, char*);
    if (r != NULL) break;
} while (1);
va_end(ap);

This will crash if you have no non-NULL argument, though, so you would better pass the number of arguments as first argument:

char *first(int nargs, ...)
{
    char *r = NULL;

    va_start(ap, nargs);
    for( ; nargs; nargs--) {
        r = va_arg(ap, char*);
        if (r != NULL) break;
    }
    va_end(ap);

    return r;
}

first(4, q, w, e, r);

Alternatively, use a sentinel:

char *first(char *first, ...)
{
    char *r = first;

    va_start(ap, first);
    while (!r) {
        r = va_arg(ap, char*);
    }
    va_end(ap);

    return r == &sentinel ? NULL : r;
}

char sentinel; // its address is unique among other pointers

first(q, w, e, r, &sentinel);
like image 148
Arnaud Le Blanc Avatar answered Jan 02 '23 13:01

Arnaud Le Blanc