Strange compiler warning C: warning: ‘struct’ declared inside parameter list

To understand why the compiler complains, you need to know two things about C “struct”s:

  • they are created (as a declared, but not yet defined, type) as soon as you name them, so the very first occurrence of struct lol creates a declaration
  • they obey the same “declaration scope” rules as ordinary variables

(struct lol { declares and then begins defining the structure, it’s struct lol; or struct lol * or something else that does not have the open-brace that stops after the “declare” step.)

A struct type that is declared but not yet defined is an instance of what C calls an “incomplete type”. You are allowed to use pointers to incomplete types, as long as you do not attempt to follow the pointer:

struct lol *global_p;
void f(void) {
    use0(global_p);     /* this is OK */
    use1(*global_p);       /* this is an error */
    use2(global_p->field); /* and so is this */
}

You have to complete the type in order to “follow the pointer”, in other words.

In any case, though, consider function declarations with ordinary int parameters:

int imin2(int a, int b); /* returns a or b, whichever is smaller */
int isum2(int a, int b); /* returns a + b */

Variables named a and b here are declared inside the parentheses, but those declarations need to get out of the way so that the the next function declaration does not complain about them being re-declared.

The same thing happens with struct tag-names:

void gronk(struct sttag *p);

The struct sttag declares a structure, and then the declaration is swept away, just like the ones for a and b. But that creates a big problem: the tag is gone and now you can’t name the structure type ever again! If you write:

struct sttag { int field1; char *field2; };

that defines a new and different struct sttag, just like:

void somefunc(int x) { int y; ... }
int x, y;

defines a new and different x and y at the file-level scope, different from the ones in somefunc.

Fortunately, if you declare (or even define) the struct before you write the function declaration, the prototype-level declaration “refers back” to the outer-scope declaration:

struct sttag;
void gronk(struct sttag *p);

Now both struct sttags are “the same” struct sttag, so when you complete struct sttag later, you’re completing the one inside the prototype for gronk too.


Re the question edit: it would certainly have been possible to define the action of struct, union, and enum tags differently, making them “bubble out” of prototypes to their enclosing scopes. That would make the issue go away. But it wasn’t defined that way. Since it was the ANSI C89 committee that invented (or stole, really, from then-C++) prototypes, you can blame it on them. 🙂

Leave a Comment