CompSci-PVT
CompSci-PVT

Reputation: 1403

Why are typedef names used twice in struct declaration in C?

While researching queues in C, I came across an example similar to the below. Why is the struct named both at the beginning of the curly braces and after? Why is struct type used again inside of the struct when adding an item of the same type? Are these things redundant or is there a point?

typedef void* vpoint_t;

typedef struct queue_item_t{
  vpoint_t void_item;
  struct queue_item_t* next;
} queue_item_t;

Upvotes: 22

Views: 7312

Answers (4)

John Bode
John Bode

Reputation: 123598

Let's change the declaration a little bit to make the discussion easier to follow:

typedef struct queue_item {
  vpoint_t void_item;
  struct queue_item* next;
} QueueItemType;

C supports several different name spaces; one name space is reserved for tag names on unions, structures, and enumeration types. In this case, the tag name is queue_item. Another name space is reserved for regular identifers, including typedef names like QueueItemType.

The next member is being used to point to another instance of type struct queue_item (i.e., the next item in the queue). It's declared as a pointer to struct queue_item for two reasons:

  • A struct type cannot contain an instance of itself; for one thing, the type would have to be infinitely large (struct queue_item contains a member next, which is a struct queue_item that contains a member next, which is a struct queue_item that contains a member next, ad infinitum);

  • The struct type definition isn't complete until the closing }, and you can't declare an instance of an incomplete type. However, you can declare a pointer to an incomplete type, which we do below:

    struct queue_item *next;

Why not use QueueItemType *next; instead of struct queue_item *next? Again, the struct type definition isn't complete at the point next is being declared; the typedef name QueueItemType doesn't exist yet. However, the tag name queue_item is already visible to the compiler, so we can declare pointers using the type struct queue_item.

Since tag names and typedef names occupy different name spaces, it's possible to use the same name for both the tag name and the typedef name without a collision. The compiler disambiguates between the two by the presence of the struct keyword.

Upvotes: 8

Jonathon Reinhart
Jonathon Reinhart

Reputation: 137547

typedef struct queue_item_t {     // 1
  vpoint_t void_item;
  struct queue_item_t* next;      // 2
} queue_item_t;                   // 3

First of all, note that this entire statement is defining a typedef.

3) Is saying that the new type we are in the process of defining (via typedef) is going to be named queue_item_t.

1) The name of the structure (which is being given a new name, as we go), is named struct queue_item_t. That's it's full name, including struct at the front.

2) Because the new type doesn't yet exist (remember, we're still in the process of defining it), we have to use the only name it has thus far, which is struct queue_item_t, from 1).


Note that you can have anonymous struct definitions, which allow you to omit the name from 1). A simple example:

typedef struct {
   int x, y, z;
} vector3;

In your example however, since we need the structure to be able to refer to itself, the next pointer must have a type that's already defined. We can do that by forward declaring the struct, typedefing it, then defining the struct using the typedefd type for next:

struct _queue_item;                           // 4

typedef struct _queue_item queue_item_t;      // 5

struct _queue_item {                          // 6
  vpoint_t void_item;
  queue_item_t* next;                         // 7
}

4) Declare that struct _queue_item exists, but don't yet provide a definition for it.

5) Typedef queue_item_t to be the same as struct _queue_item.

6) Give the definition of the structure now...

7) ...using our typedef'd queue_item_t.


All that being said... In my opinion, please don't use typedefs for structs.

struct queue_item {
    void *data;
    struct queue_item *next;
}

is simple and complete. You can manage to type those six extra characters.

From the Linux Kernel coding style:

Chapter 5: Typedefs

Please don't use things like "vps_t". It's a mistake to use typedef for structures and pointers. When you see a

  vps_t a;

in the source, what does it mean? In contrast, if it says

  struct virtual_container *a;

you can actually tell what "a" is.

There are exceptions, which you can read about.


Some recent related questions:

Upvotes: 34

epx
epx

Reputation: 1096

"struct foo {...} " is one thing. It defines a struct, you need to type "struct foo" in every place you use it.

"typedef ... foo" defines a new type, so you just type "foo" where you use it.

"typedef struct foo {...} foo" is an idiom so you can use both, most probably just "foo" to save keystrokes and visual pollution.

Upvotes: 1

Paul Ogilvie
Paul Ogilvie

Reputation: 25286

First, I suggest to NEVER use a struct name and identical type name. Use typedef struct QUEUE_ITEM {...} queue_item_t;

As to the question: if you want to make a "recursive data structure", that is, a data structure that has pointers to instances of itself, then you must be able to tell the compiler "This field is a pointer to one of ourselves. You don't know what we look like yet completely, because I am still defineing it, so just reserve space for a pointer". To do that you declare

struct T {
    ...
    struct T *ptr;
    ....
};

With the final } queue_item_t; you create a new name for the structure.

Upvotes: 0

Related Questions