Why can you use a typedef'd struct before the typedef?

110 views Asked by At

Here's a MWE:

#include <stdlib.h>

typedef struct node_type {
    struct node* next;
    struct node* prev;
} node;

int main(int argc, char* argv[]) {
    node* n = (node*)malloc(sizeof(node));
}

Not having struct before node* in the struct definition will error; node has not yet been defined. However, struct node* works fine, even though only struct node_type has been defined at this point. Why does this work?

Intuitively, it compiles if the struct is defined with struct node_type* for its fields, or if there is an explicit typedef before and the fields are of type node*. However, I don't understand why an in-line typedef allows the fields to be of type struct node* but not of type node*.

3

There are 3 answers

0
Vlad from Moscow On BEST ANSWER

Not having struct before node* in the struct definition will error; node has not yet been defined. However, struct node* works fine, even though only struct node_type has been defined at this point.

In this typedef declaration

typedef struct node_type {
    struct node* next;
    struct node* prev;
} node;

there are declared two structure types. The first one, struct node_type, is a complete type, and the second one, struct node, is an incomplete type.

Apart from these declarations, the code introduces an alias node for the type specifier struct node_type.

You may write

node* n = (node*)malloc(sizeof(node));

but you may not write for example

n->next = ( node * )malloc( sizeof( node ) );

or

n->next = malloc( sizeof( struct node ) );

because the type struct node is an incomplete type. Its size is unknown. And struct node * and node * are incompatible pointer types.

As for your question then if you will write for example

typedef struct node_type {
    node* next;
    node* prev;
} node;

then in these member declarations

node* next;
node* prev;

the name node is not defined yet. So, the compiler issues an error message.

6
gulpr On

You can use node without struct if you declare it before use in struct declaration using the forward declaration which will declare an incomplete type:

typedef struct node_type node;

struct node_type{
    node* next;
    node* prev;
};

int main(int argc, char* argv[]) {
    node* n =malloc(sizeof(*n));
}

https://godbolt.org/z/KoWdfG8dn

Why can you use a typedef'd struct before the typedef?

Because at this point this new type is not known to the compiler.

Not having struct before node* in the struct definition will error; node has not yet been defined. However, struct node* works fine, even though only struct node_type has been defined at this point. Why does this work?

because you forward declare the new struct with the tag node. It does nothing in common with your structs here. Example:

typedef struct node_type {
    struct something* next;
    struct something_else* prev;
} node;

In the example above you declare two incomplete types struct something and struct something_else

From GNU C manual:

A type that has not been fully defined is called an incomplete type. Structure and union types are incomplete when the code makes a forward reference, such as struct foo, before defining the type. An array type is incomplete when its length is unspecified. You can’t use an incomplete type to declare a variable or field, or use it for a function parameter or return type. The operators sizeof and _Alignof give errors when used on an incomplete type. However, you can define a pointer to an incomplete type, and declare a variable or field with such a pointer type. In general, you can do everything with such pointers except dereference them.

https://godbolt.org/z/vbxMG5zK6

0
Eric Postpischil On

However, struct node* works fine, even though only struct node_type has been defined at this point. Why does this work?

C has separate name spaces for ordinary identifiers and for structure tags (and union tags and enumeration tags).

typedef type name defines name to be an ordinary identifier for type. You can use name for type after this declaration but not before it. (Specifically, the point at which name can be used is the end of its declarator, a part fo the C grammar including the name and any parentheses for function parameters or brackets for array sizes that are with it.)

struct tag declares tag to be a tag for the structure as soon as it appears.

In this code:

typedef struct node_type {
    struct node* next;
    struct node* prev;
} node;

As soon as struct node appears, node is known as a tag, so it can be used as a name for a structure, even if the full definition of the structure is not known. However, here you have struct node_type and struct node referring to different things. You probably meant to use the same tag:

typedef struct node {
    struct node* next;
    struct node* prev;
} node;

Now, where struct node appears in the second line (and the third), node is already known as a tag for a structure from the first line. So struct node in the second line and the third line refer to the same type as struct node in the first line.

In contrast, the node as an ordinary identifier in the typedef does not appear until the last line, so it is not known until later. So the second and third lines cannot use node as a type name.

Although struct node may be known as a type, its full definition is not initially known. Because of this, you cannot declare a member of that type, such as struct node x;. However, you can declare a pointer to the structure, struct node *next;, because the pointer type is already known. All pointers to structures have the same size and representation. So a compiler does not need to know what is in a structure to know how to make a pointer to it.