22

I'm trying to typedef a struct which contains a pointer to another of the same type.

Thats what I thought would be the best version:

typedef struct Element
{
    char value;
    struct Element *next;
} Element;

Why is that variant also compiling + executing?:

typedef struct
{
    char value;
    struct Element *next;
} Element;

To describe the first I'd say: "Name struct Element Element now" and the second as: "Take this anonymous struct and call it Element"

But why can I still declare a struct Element (inside the struct) in the second case?

(Working in GCC and MSVC)

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
ordag
  • 2,497
  • 5
  • 26
  • 35
  • 1
    in the second case your *next is actually turning into a struct struct {}, you need to read up on what type define actually does. – bigkm Sep 19 '11 at 17:30
  • @bigkm Replacing `struct Element *next;` with `Element *next;` is reported as an error. Or what do you mean? – ordag Sep 19 '11 at 17:34
  • 1
    @bigkm: What does `struct struct {}` mean? In the second case, `next` is a pointer to `struct Element`; the problem is that `struct Element` is an incomplete type. – Keith Thompson Sep 19 '11 at 17:45
  • How to version: http://stackoverflow.com/questions/3988041/struct-containing-pointers-to-itself – Ciro Santilli OurBigBook.com May 03 '16 at 09:17

3 Answers3

23

In the first case, your struct has two equivalent names: struct Element (where Element is a struct tag) and Element (where Element is a typedef, an alias for an existing type).

In the second case, you just didn't define a tag for the struct. Normally that would be perfectly valid, but here you're referring to the nonexistent type struct Element in the declaration of the next member.

In that context, struct Element is an incomplete type. You can't declare objects of incomplete types, but you can declare pointers to them.

The declaration

typedef struct
{
    char value;
    struct Element *next;
} Element;

is legal, but it doesn't make next a pointer to the enclosing type. It makes it a pointer to some incomplete type, and you won't be able to refer to it until and unless you declare the full type.

Your second declaration is one of the plethora of things that don't make sense, but are still legal C.

You might consider just omitting the typedef and consistently referring to the type as struct Element. As lot of people like the convenience of having a one-word name for a structure type, but my own personal opinion is that there's not much benefit to that (unless the type is truly opaque, i.e., users of the type don't even know it's a struct). It's a matter of style.

Note that you need to refer to the type as struct Element, not Element, within the definition itself, since the typedef name Element isn't visible yet.

The fact that the struct tag and the typedef have the same name may seem confusing, but it's perfectly legititimate. Struct tags and typedefs are in separate namespaces (in the C sense, not the C++ sense); a struct tag can only appear immediately after the struct keyword.

Another alternative is to separate the typedef from the struct definition:

typedef struct Element Element;

struct Element {
    char value;
    Element *next;
};

(You can use an incomplete type name in a typedef.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Ah, wow, that makes sense, didn't know that about pointers to incomplete types. Thank you. – ordag Sep 19 '11 at 17:42
4

Your first variant is correct. Your second variant doesn't do what it appears to do.

In C, it's valid to forward-declare a struct type anywhere, even in the middle of declaring something else. (The scoping rules for such declarations-in-passing are confusing to the point where I'm not going to try to explain them -- suffice to say that you should avoid doing so.) That is why you don't get an error on the second construct. But what it means to the compiler is this:

struct _Anonymous_1 // name not actually accessible to code
{
    char value;
    struct Element *next;
};
typedef struct _Anonymous_1 Element;

After this code, the type "struct Element" is completely unrelated to the type "Element", and has not been fully declared. If you were to attempt to use that type, e.g. in

char cadr(Element *cons)
{
    return cons->next->value;
}

the compiler would not be happy:

test.c: In function ‘cadr’:
test.c:9:22: error: dereferencing pointer to incomplete type

An alternative to your first variant, that lets you use 'Element' instead of 'struct Element' everywhere, including inside the definition of the type, is

typedef struct Element Element;
struct Element
{
    char value;
    Element *next;
};

But in C there is no way to avoid having to manually make sure that "struct Element" is the same thing as "Element". If you don't want to have to deal with it, C++ is waiting for you over there ⟶

zwol
  • 135,547
  • 38
  • 252
  • 361
  • Thank you. You'll always learn something new in that language which "wow"s you. (Your answer is a few seconds too late, so i'll accept the first, but this one defin. helped understanding more) – ordag Sep 19 '11 at 17:48
  • Oh and especially your third version with the typedef before ;-) – ordag Sep 19 '11 at 17:50
  • 1
    Just a reminder: you can't safely use a name like "_Anonymous_1" in your own code, since identifiers starting with underscores are reserved to the implementation. I know you used it in pseudo-code intended to show what goes on within the compiler, so this isn't a correction, just a clarification. – Keith Thompson Sep 19 '11 at 17:51
  • Yes, that was quite deliberate. (I considered throwing a dollar sign in there, but decided it would confuse more than it would help.) – zwol Sep 19 '11 at 18:33
0

To store a pointer to a struct, the compiler doesn't need to know its contents or size -- just the size of the pointer.

In your first example, struct Element is an incomplete type until after the structure definition, but that works because you're only declaring a pointer to it and not an instance of the struct itself.

In your second example, you don't declare a struct Element structure at all (struct Element and Element are not the same thing). Though you can still include the pointer in the structure, it does NOT refer to the same type, it refers to a struct Element that hasn't been defined. The struct definition in your typedef is an anonymous struct, so you'll be able to refer to it with Element (without the struct keyword) only. So the second example won't work as intended.

Dmitri
  • 9,175
  • 2
  • 27
  • 34