18

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;
CompSci-PVT
  • 1,303
  • 2
  • 10
  • 23
  • 2
    Possible answer: http://stackoverflow.com/questions/252780/why-should-we-typedef-a-struct-so-often-in-c – Paulo Sep 04 '15 at 19:31
  • 1
    Suggest to NEVER use a struct name and identical type name. Use `typedef struct QUEUE_ITEM {...} queue_item_t;` – Paul Ogilvie Sep 04 '15 at 19:33
  • 5
    Voted to re-open, because the dup'd question wasn't asking the same exact thing. – Jonathon Reinhart Sep 04 '15 at 19:33
  • @JonathonReinhart Exactly! I was typeing the answer when I get a menu bar notification saying the question was closed – Jack Sep 04 '15 at 19:34
  • Me too, lost my time composing an answer – epx Sep 04 '15 at 19:36
  • 2
    @PaulOgilvie You shouldn't end your types with `_t` because it's reserved by POSIX. – Uyghur Lives Matter Sep 04 '15 at 19:37
  • There are even weirder usages out there. BSDs usually have `typedef struct foo foo_t` which is also what I'd use. openssl uses `typedef struct foo_st FOO`. LZMA has `struct foo_s foo`. clang uses `struct foo_t foo_t`. Name your favorite poison. – dhke Sep 04 '15 at 19:50
  • @cpbunz, agree, I prefer `t_...` but I tried to stay within the names used by the OP. – Paul Ogilvie Sep 04 '15 at 22:22

4 Answers4

31
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:

Community
  • 1
  • 1
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
  • Note: I think everything here about `struct` applies to `union`. – chux - Reinstate Monica Sep 04 '15 at 20:12
  • Disciplined use of typedefs is fine. Linus doesn't like it; that matters only if writing linux kernel code. What's crazy is mixing "typedef struct foo {..} foo_t" with "typedef struct bar { ...} * bar_t" and similar code obfuscation. Or nonsense like "#define foo_t struct foo". – Arlie Stephens Sep 04 '15 at 20:52
8

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.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

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.

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
0

"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.

epx
  • 1,066
  • 9
  • 17
  • 1
    `typedef struct foo { struct foo *next } foo;` compiles, `typedef struct { foo *next } foo;` does not compile, so the purpose of the idiom is to allow a pointer-to-the-struct to be included in the struct itself. – user3386109 Sep 04 '15 at 19:44