The different uses reflect whether the type is used in interfaces (only), or whether the structure information must be known.
The notation:
typedef struct some_obj_s some_obj_t;
tells the compiler that there's a structure type with tag some_obj_s
and typedef name some_obj_t
. If this type is an opaque type for users, there's no need to specify the details of the structure. Functions that take a some_obj_t *
parameter (possibly qualified) or return such values can be declared and used without further information. (You can even declare extern
variables of this sort of type since the compiler mainly needs an address.) The type is incomplete (also known as opaque), but can be completed.
Somewhere in the implementation, you'd find a definition of the structure type (except in the most unusual of circumstances), such as:
struct some_obj_s
{
...members defined...
};
Where this declaration (definition) is in scope, you can create variables of type some_obj_t
or struct some_obj_s
, as well as pointers to the type. The code can use the members of the structure.
The notation:
typedef struct /* no tag */
{
...
} another_obj_t;
defines a tagless struct type. The details of the type are provided, so the type is complete.
You may also see:
typedef struct yao_s
{
...members defined...
} yao_t;
This both declares the structure type with its tag and the typedef.
The separate typedef has an advantage with self-referencing structures, such as lists:
typedef struct GenericList GenericList;
struct GenericList
{
void *data;
GenericList *next;
GenericList *prev;
};
Without the separate typedef
line, you'd have to write struct GenericList
inside the structure. It means the same thing; you could still write it; but with the separate typedef, you can use the same notation in the structure as you will use later referring to it.
Note that POSIX reserves type names ending _t
for the implementation. That means it can be dangerous to use that convention in your own code.
There is no need to use separate names for the tag and typedef (as demonstrated by the GenericList
example), though the _s
and _t
suffixes are a fairly well known convention too. C++ automatically makes the tag into a type name without needing an explicit typedef (but the explicit typedef is allowed), so I usually use the same name for tag and type.