The one time the long-winded notation matters is where the sequence is inside a function and there's a type struct foo
defined outside the function too. Then the plain struct foo;
line shadows the out-of-function definition. If you don't include that line, you can end up with incompatible pointer types, both called foobar
, in structures that look the same, leading to horrible confusion. (See also Which part of the C standard allows this code to compile? and Does the C standard consider that there are one or two 'struct uperms_entry' types in this header? for some more examples.)
What? You're confused by what I said? OK; here comes an example. Note this is contrived; it is bad style to redefine a structure type inside a function (and the example of the problem depends on you doing so).
#include <stdlib.h>
struct foo; // This line is optional; it is harmless if omitted
typedef struct foo *foobar;
struct foo
{
void *data;
foobar example[]; // Flexible array member; array of pointers.
};
extern void some_function(void);
void some_function(void)
{
struct foo; // This line is crucial!
typedef struct foo *foobar;
struct foo
{
foobar *array[3];
void *pointer;
};
foobar x = calloc(sizeof(*x), 1);
foobar y = malloc(sizeof(*y));
y->array[0] = x->array[1];
y->array[1] = x->array[2];
y->array[2] = x->array[0];
y->pointer = x->array[1];
free(x);
free(y);
}
With the struct foo;
line in the function, the code compiles cleanly:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
> -Wstrict-prototypes -pedantic -c st97.c
$
The struct foo;
line inside the function says "forget about any previous struct foo
type; there's a new one in this scope". The forgetfulness only lasts for the scope, of course.
With the struct foo;
line commented out in the function, the code fails to compile noisily:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
> -Wstrict-prototypes -pedantic -c st97.c
st97.c: In function ‘some_function’:
st97.c:28:6: error: ‘struct foo’ has no member named ‘array’
y->array[0] = x->array[1];
^~
st97.c:28:20: error: ‘struct foo’ has no member named ‘array’
y->array[0] = x->array[1];
^~
st97.c:29:6: error: ‘struct foo’ has no member named ‘array’
y->array[1] = x->array[2];
^~
st97.c:29:20: error: ‘struct foo’ has no member named ‘array’
y->array[1] = x->array[2];
^~
st97.c:30:6: error: ‘struct foo’ has no member named ‘array’
y->array[2] = x->array[0];
^~
st97.c:30:20: error: ‘struct foo’ has no member named ‘array’
y->array[2] = x->array[0];
^~
st97.c:31:6: error: ‘struct foo’ has no member named ‘pointer’
y->pointer = x->array[1];
^~
st97.c:31:20: error: ‘struct foo’ has no member named ‘array’
y->pointer = x->array[1];
^~
$
At the point when the type foobar
is defined inside the function, the name struct foo
refers to the type defined outside the function, so the structure contains an array of pointers to pointers to the external struct foo
, and that structure has members data
and example
and not members array
and pointer
.
As I said, this is appalling style. So, you say "what about if I use struct foo
inside the structure definition in the function:
#include <stdlib.h>
struct foo;
typedef struct foo *foobar;
struct foo
{
void *data;
foobar example[]; // Flexible array member; array of pointers.
};
extern void some_function(void);
void some_function(void)
{
//struct foo;
typedef struct foo *foobar;
struct foo
{
struct foo *array[3];
void *pointer;
};
foobar x = calloc(sizeof(*x), 1);
foobar y = malloc(sizeof(*y));
y->array[0] = x->array[1];
y->array[1] = x->array[2];
y->array[2] = x->array[0];
y->pointer = x->array[1];
free(x);
free(y);
}
This is still a problem. The internal struct foo
is not complete until the }
, so the struct foo *example[3]
still refers to the external struct foo
— and you get the same error messages.
The short moral is "do not redefine a structure type inside a function".
If you must do so, note that it can only really be used in the function; it is hard to pass it to other functions — not quite impossible, but you really have to know what you're doing.
And the longer moral is "occasionally, under dubious circumstances, the struct foo;
line on its own can be crucial".
Note that even if the structure body definitions are actually the same, technically, they're different types (both called struct foo
though). This is confusing to everyone — don't do it, please! Actually, let's make that more imperative:
Don't do it!