Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

va_arg with pointers

I want to initialize a linked list with pointer arguments like so:

/* 
 * Initialize a linked list using variadic arguments
 * Returns the number of structures initialized
 */
int init_structures(struct structure *first, ...) 
{
    struct structure *s;
    unsigned int count = 0;
    va_list va;
    va_start(va, first);

    for (s = first; s != NULL; s = va_arg(va, (struct structure *))) {
        if ((s = malloc(sizeof(struct structure))) == NULL) {
            perror("malloc");
            exit(EXIT_FAILURE);
        }
        count++;
    }

    va_end(va);

    return count;
}

The problem is that clang errors type name requires a specifier or qualifier at va_arg(va, (struct structure *)), and says that the type specifier defaults to int. It also notes instantiated form at (struct structure *) and struct structure *. This, what seems to be getting assigned to s is int (struct structure *).

It compiles fine when parentheses are removed from (struct structure *), but the structures that are supposed to be initialized are inaccessible.

Why is int assumed when parentheses are around the type argument passed to va_arg? How can I fix this?

like image 749
Yktula Avatar asked May 16 '10 21:05

Yktula


2 Answers

va_arg is a macro on many systems, and evidently the parentheses around struct structure * causes the macro to expand so something unparseable. So don't do that.

This has nothing to do with the reason that your initialized structures are "inaccessible". You are allocating structures and assigning them to s, but s is a local variable. You can't affect a value in the caller by assigning to a local variable. To accomplish what you want to do, the caller needs to pass a pointer-to-a-pointer, which you can then initialize

int init_structures(struct structure **first, ...) 
{
    struct structure **s;
    unsigned int count = 0;
    va_list va;
    va_start(va, first);

    for (s = first; s != NULL; s = va_arg(va, struct structure **)) {
        if ((*s = malloc(sizeof(struct structure))) == NULL) {
            perror("malloc");
            exit(EXIT_FAILURE);
        }
        count++;
    }

    va_end(va);

    return count;
}

And the caller should do:

struct structure *a, *b;
init_structures(&a, &b, NULL);
like image 50
JSBձոգչ Avatar answered Sep 18 '22 01:09

JSBձոգչ


§7.15.1.1 (The va_arg macro) of C99 requires:

The parameter type shall be a type name specified such that the type of a pointer to an object that has the specified type can be obtained simply by postfixing a * to type.

That is why parentheses are not permitted here.

Other answers have explained why you need to pass in struct structure ** and assign the malloc result to *s.

like image 22
Matthew Flaschen Avatar answered Sep 21 '22 01:09

Matthew Flaschen