views:

206

answers:

3

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?

+1  A: 

You can't put parens around the type in va_arg. You don't need to. va_arg is a strange beast. Practically, it's a CPP macro that expands into some sort of per-compiler magic. If you look at the expansion of it with your compiler, you will see how your compiler does it. Whatever it is, it don't like parens.

bmargulies
+3  A: 

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);
JSBangs
`va_arg` is required to be a macro.
Matthew Flaschen
+3  A: 

§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.

Matthew Flaschen