The common idiom is using both:

typedef struct S { 
    int x; 
} S;

They are different definitions. To make the discussion clearer I will split the sentence:

struct S { 
    int x; 
};

typedef struct S S;

In the first line you are defining the identifier S within the struct name space (not in the C++ sense). You can use it and define variables or function arguments of the newly defined type by defining the type of the argument as struct S:

void f( struct S argument ); // struct is required here

The second line adds a type alias S in the global name space and thus allows you to just write:

void f( S argument ); // struct keyword no longer needed

Note that since both identifier name spaces are different, defining S both in the structs and global spaces is not an error, as it is not redefining the same identifier, but rather creating a different identifier in a different place.

To make the difference clearer:

typedef struct S { 
    int x; 
} T;

void S() { } // correct

//void T() {} // error: symbol T already defined as an alias to 'struct S'

You can define a function with the same name of the struct as the identifiers are kept in different spaces, but you cannot define a function with the same name as a typedef as those identifiers collide.

In C++, it is slightly different as the rules to locate a symbol have changed subtly. C++ still keeps the two different identifier spaces, but unlike in C, when you only define the symbol within the class identifier space, you are not required to provide the struct/class keyword:

 // C++
struct S { 
    int x; 
}; // S defined as a class

void f( S a ); // correct: struct is optional

What changes are the search rules, not where the identifiers are defined. The compiler will search the global identifier table and after S has not been found it will search for S within the class identifiers.

The code presented before behaves in the same way:

typedef struct S { 
    int x; 
} T;

void S() {} // correct [*]

//void T() {} // error: symbol T already defined as an alias to 'struct S'

After the definition of the S function in the second line, the struct S cannot be resolved automatically by the compiler, and to create an object or define an argument of that type you must fall back to including the struct keyword:

// previous code here...
int main() {
    S(); 
    struct S s;
}
Answer from David Rodríguez - dribeas on Stack Overflow
Top answer
1 of 12
1361

The common idiom is using both:

typedef struct S { 
    int x; 
} S;

They are different definitions. To make the discussion clearer I will split the sentence:

struct S { 
    int x; 
};

typedef struct S S;

In the first line you are defining the identifier S within the struct name space (not in the C++ sense). You can use it and define variables or function arguments of the newly defined type by defining the type of the argument as struct S:

void f( struct S argument ); // struct is required here

The second line adds a type alias S in the global name space and thus allows you to just write:

void f( S argument ); // struct keyword no longer needed

Note that since both identifier name spaces are different, defining S both in the structs and global spaces is not an error, as it is not redefining the same identifier, but rather creating a different identifier in a different place.

To make the difference clearer:

typedef struct S { 
    int x; 
} T;

void S() { } // correct

//void T() {} // error: symbol T already defined as an alias to 'struct S'

You can define a function with the same name of the struct as the identifiers are kept in different spaces, but you cannot define a function with the same name as a typedef as those identifiers collide.

In C++, it is slightly different as the rules to locate a symbol have changed subtly. C++ still keeps the two different identifier spaces, but unlike in C, when you only define the symbol within the class identifier space, you are not required to provide the struct/class keyword:

 // C++
struct S { 
    int x; 
}; // S defined as a class

void f( S a ); // correct: struct is optional

What changes are the search rules, not where the identifiers are defined. The compiler will search the global identifier table and after S has not been found it will search for S within the class identifiers.

The code presented before behaves in the same way:

typedef struct S { 
    int x; 
} T;

void S() {} // correct [*]

//void T() {} // error: symbol T already defined as an alias to 'struct S'

After the definition of the S function in the second line, the struct S cannot be resolved automatically by the compiler, and to create an object or define an argument of that type you must fall back to including the struct keyword:

// previous code here...
int main() {
    S(); 
    struct S s;
}
2 of 12
228

struct and typedef are two very different things.

The struct keyword is used to define, or to refer to, a structure type. For example, this:

struct foo {
    int n;
};

creates a new type called struct foo. The name foo is a tag; it's meaningful only when it's immediately preceded by the struct keyword, because tags and other identifiers are in distinct name spaces. (This is similar to, but much more restricted than, the C++ concept of namespaces.)

A typedef, in spite of the name, does not define a new type; it merely creates a new name for an existing type. For example, given:

typedef int my_int;

my_int is a new name for int; my_int and int are exactly the same type. Similarly, given the struct definition above, you can write:

typedef struct foo foo;

The type already has a name, struct foo. The typedef declaration gives the same type a new name, foo.

The syntax allows you to combine a struct and typedef into a single declaration:

typedef struct bar {
    int n;
} bar;

This is a common idiom. Now you can refer to this structure type either as struct bar or just as bar.

Note that the typedef name doesn't become visible until the end of the declaration. If the structure contains a pointer to itself, you have use the struct version to refer to it:

typedef struct node {
    int data;
    struct node *next; /* can't use just "node *next" here */
} node;

Some programmers will use distinct identifiers for the struct tag and for the typedef name. In my opinion, there's no good reason for that; using the same name is perfectly legal and makes it clearer that they're the same type. If you must use different identifiers, at least use a consistent convention:

typedef struct node_s {
    /* ... */
} node;

(Personally, I prefer to omit the typedef and refer to the type as struct bar. The typedef saves a little typing, but it hides the fact that it's a structure type. If you want the type to be opaque, this can be a good thing. If client code is going to be referring to the member n by name, then it's not opaque; it's visibly a structure, and in my opinion it makes sense to refer to it as a structure. But plenty of smart programmers disagree with me on this point. Be prepared to read and understand code written either way.)

(C++ has different rules. Given a declaration of struct blah, you can refer to the type as just blah, even without a typedef. Using a typedef might make your C code a little more C++-like -- if you think that's a good thing.)

Top answer
1 of 3
8

There is no such thing as a "typedef struct".

Part the first: structure types

struct introduces a structure, which is an aggregate datatype consisting of a set of named members. (Arrays are also aggregates, but they consist of a number of identical members which are indexed. Unions have a set of member names, but can only contain one member at a time, so they are not aggregates. You probably didn't need to know that.)

Structure types usually have tags, so the actual typename will be something like struct Triangle. The tag (Triangle) is not in the same namespace as identifiers, so there is no problem using a tag which is also used for another purpose. Some people like to append tags with _s and _u, indicating that they are structure or union tags respectively, but that's not my style; I prefer to name types in CamelCase, and for me a structure or union tag is standing in for a typename. But of course you are free to use your own conventions.

If you use struct SomeTag in a program, you are effectively declaring that there is a structure whose tag is SomeTag. You're not required to fill in the declaration by naming or describing the structure's members, unless you need to refer to them in your code. A structure whose members have not (yet) been declared is called incomplete, but it can still be used as part of a pointer type because the C standard guarantees that all structure pointers have the same format, regardless of the contents of the structure. (That doesn't make them interchangeable, but it does mean that the compiler knows how big the pointers are.) A structure which never has its members defined and which is used only as the the target of a pointer type is called opaque.

You can complete the declaration of a structure by adding a block of member declarations. So

Copystruct Triangle {
    int a,b,c;
};

first declares that there is a structure whose name is struct Triangle, and then fills in the definition of that structure by declaring three named members which are all ints.

Union declarations and definitions are all very similar, by the way.

A structure definition can be used in a declaration as though it were a type name. Or to put it another way, you can declare the tag for a structure type, immediately fill in the fields, and then declare one or more variables of that type:

Copystruct Triangle { int a, b, c; } aTriangle, anotherTriangle;

That's not a very common style, but it's important to know that the syntax is possible.

Finally, it is legal define a structure without giving it a tag. Tagless structure types have a quirk: normally no two structure types can have the same tag, but all tagless structures are distinct. That means that you can declare a structure type which effectively has no name, and which is different from any other structure type, even a structure type with exactly the same members. That can be slightly useful if you have an aggregate which will only ever have one instance (a "singleton"), although I wouldn't really ever use this style myself. But after a small detour, we'll see another use for this feature.

Part the second: type aliases

C type names can be quite complicated, since they can be built up out of pieces. For example, const struct Triangle*[8] is an array of eight members, each of which is a pointer to an unmodifiable struct Triangle. double (*)(const struct Triangle*[8]) is a function which accepts one such array as an argument (or, more accurately, which accepts a pointer to the first element of such an array, because of array-to-pointer decay. But that's not relevant here.)

To make complex types a bit easier to use, C allows you to declare an alias name for a type. Aliases are declared with typedef and otherwise look exactly like the declaration of a variable. So, for example, you can declare a variable of type int with

Copyint someNumber;

and thus you can declare an alias for the type int with

Copytypedef int someType;

Similarly, you could declare an array of eight pointers to const Triangle elements with

Copyconst Triangle* eightSlices[8];

In exactly the same way, you can declare a name for the type of such an array with:

Copytypedef const Triangle* EightSlices[8];

Note that the name of the type goes exactly where the name of the object would go, which can be somewhere in the middle of the declaration.

Part the third: both of the above in one statement

As a simple example of declaring type aliases, here's how you declare an alias for a structure type:

An incomplete structure definition:

Copytypedef struct Triangle Triangle;

Or a complete structure definition:

Copytypedef struct Triangle {
   int a, b, c;
} Triangle;

Or both, separately (and these could go in either order):

Copytypedef struct Triangle Triangle;
struct Triangle {
    int a, b, c;
};

Remember that structure tags and other identifiers (such as type aliases) are in different namespaces, so there is no conflict between the two uses of Triangle above. Some programmers feel that it is necessary to distinguish between them, even though one of them can only be used immediately after the word struct and the other one cannot be used following the word struct. Others -- and I think you can guess that I fall into this crowd -- find it convenient to deliberately use the same name for both, relying on the presence or absence of the word struct to let us know whether it is a type alias or a tag. (And, more commonly, to indicate that we have no intention of ever again using the tag.)

So, back to my opening comment:

There is no such thing as a "typedef struct".

And there isn't. What we have here is a very ordinary struct, declared and defined. And we have a very ordinary type alias which gives an alternate name for that struct. And that's it.

Note that you can give an alias to an anonymous type (such as a tagless structure type), after which the type is no longer anonymous. So there are some people who would leave out the tag in the above definition:

Copytypedef struct {
    int a, b, c;
} Triangle;

That looks a lot like the singleton structure type mentioned above, but since it is a type it can be used to declare multiple instances. But I don't actually recommend this style.

To each their own: an ignorable appendix

Everyone has their own style preferences, and most of these preferences are valid. Most of us have worked on more than one project, and since every project tends to develop its own style guide, we need to learn how to accept and use different styles in different projects. But I think most of us have some style with which we feel most comfortable, which we will revert to when we're writing code just for ourselves (or when we're starting a project with the intention of attracting colleagues prepared to conform to our style). And what I've used above is my style.

In fact, I try to avoid the condensed declaration+definition+alias syntax, preferring the two-declaration version shown above:

Copytypedef struct Triangle Triangle; /* type alias */
struct Triangle {
  int a, b, c;
};                                /* type definition */

The reason I prefer that is that it lets me define types with self-referring members, such as linked lists and trees:

Copytypedef struct TriangleList TriangleList;
struct TriangleList {
    Triangle      slice;
    TriangleList* next;
};

(If I hadn't forward-aliased the type, I would have had to declare the member as struct TriangleList* next; which makes for even uglier alignment.)

Sometimes I end up with mutually referring types, and in that case, I need to gather the aliases together before any of the structure definitions. That also can be advantageous, because the alias definitions allow for opaque use of pointers to the type and can therefore be placed into a public header which does not include the type definitions at all.

But that's all just me. Feel free to ignore it.

2 of 3
7

There is no difference, typedef just removes the requirement to prefix variable declarations with struct.

Whether or not to typedef structs by default is a religious war fought mostly by people with too much time on their hands. When working inside an existing code base you should do whatever the coding standard or the surrounding code does. For your own personal code do whatever you prefer.

Discussions

c - struct and typedef - Stack Overflow
In the second version, Foo is a typedef for an unnamed struct. Although both Foo can be used in the same way in many instances there are important differences. More on stackoverflow.com
🌐 stackoverflow.com
c - Typedef struct vs struct? |Definition difference| - Stack Overflow
The following blocks are outside of main() and before every function (global scope) 1st block: struct flight { int number; int capacity; int passengers; }; With this you can create array, More on stackoverflow.com
🌐 stackoverflow.com
March 17, 2017
Why should we typedef a struct so often in C? - Stack Overflow
I have seen many programs consisting of structures like the one below typedef struct { int i; char k; } elem; elem user; Why is it needed so often? Any specific reason or applicable area? More on stackoverflow.com
🌐 stackoverflow.com
C : typedef struct name {...}; VS typedef struct{...} name; - Stack Overflow
Releases Keep up-to-date on features we add to Stack Overflow and Stack Internal. ... Find centralized, trusted content and collaborate around the technologies you use most. Learn more about Collectives ... Bring the best of human thought and AI automation together at your work. Explore Stack Internal ... typedef struct ... More on stackoverflow.com
🌐 stackoverflow.com
Top answer
1 of 8
1401

In C++, there is only a subtle difference. It's a holdover from C, in which it makes a difference.

The C language standard (C89 §3.1.2.3, C99 §6.2.3, and C11 §6.2.3) mandates separate namespaces for different categories of identifiers, including tag identifiers (for struct/union/enum) and ordinary identifiers (for typedef and other identifiers).

If you just said:

struct Foo { ... };
Foo x;

you would get a compiler error, because Foo is only defined in the tag namespace.

You'd have to declare it as:

struct Foo x;

Any time you want to refer to a Foo, you'd always have to call it a struct Foo. This gets annoying fast, so you can add a typedef:

struct Foo { ... };
typedef struct Foo Foo;

Now struct Foo (in the tag namespace) and just plain Foo (in the ordinary identifier namespace) both refer to the same thing, and you can freely declare objects of type Foo without the struct keyword.


The construct:

typedef struct Foo { ... } Foo;

is just an abbreviation for the declaration and typedef.


Finally,

typedef struct { ... } Foo;

declares an anonymous structure and creates a typedef for it. Thus, with this construct, it doesn't have a name in the tag namespace, only a name in the typedef namespace. This means it also cannot be forward-declared. If you want to make a forward declaration, you have to give it a name in the tag namespace.


In C++, all struct/union/enum/class declarations act like they are implicitly typedef'ed, as long as the name is not hidden by another declaration with the same name. See Michael Burr's answer for the full details.

2 of 8
256

In this DDJ article, Dan Saks explains one small area where bugs can creep through if you do not typedef your structs (and classes!):

If you want, you can imagine that C++ generates a typedef for every tag name, such as

typedef class string string;

Unfortunately, this is not entirely accurate. I wish it were that simple, but it's not. C++ can't generate such typedefs for structs, unions, or enums without introducing incompatibilities with C.

For example, suppose a C program declares both a function and a struct named status:

int status(); struct status;

Again, this may be bad practice, but it is C. In this program, status (by itself) refers to the function; struct status refers to the type.

If C++ did automatically generate typedefs for tags, then when you compiled this program as C++, the compiler would generate:

typedef struct status status;

Unfortunately, this type name would conflict with the function name, and the program would not compile. That's why C++ can't simply generate a typedef for each tag.

In C++, tags act just like typedef names, except that a program can declare an object, function, or enumerator with the same name and the same scope as a tag. In that case, the object, function, or enumerator name hides the tag name. The program can refer to the tag name only by using the keyword class, struct, union, or enum (as appropriate) in front of the tag name. A type name consisting of one of these keywords followed by a tag is an elaborated-type-specifier. For instance, struct status and enum month are elaborated-type-specifiers.

Thus, a C program that contains both:

int status(); struct status;

behaves the same when compiled as C++. The name status alone refers to the function. The program can refer to the type only by using the elaborated-type-specifier struct status.

So how does this allow bugs to creep into programs? Consider the program in Listing 1. This program defines a class foo with a default constructor, and a conversion operator that converts a foo object to char const *. The expression

p = foo();

in main should construct a foo object and apply the conversion operator. The subsequent output statement

cout << p << '\n';

should display class foo, but it doesn't. It displays function foo.

This surprising result occurs because the program includes header lib.h shown in Listing 2. This header defines a function also named foo. The function name foo hides the class name foo, so the reference to foo in main refers to the function, not the class. main can refer to the class only by using an elaborated-type-specifier, as in

p = class foo();

The way to avoid such confusion throughout the program is to add the following typedef for the class name foo:

typedef class foo foo;

immediately before or after the class definition. This typedef causes a conflict between the type name foo and the function name foo (from the library) that will trigger a compile-time error.

I know of no one who actually writes these typedefs as a matter of course. It requires a lot of discipline. Since the incidence of errors such as the one in Listing 1 is probably pretty small, you many never run afoul of this problem. But if an error in your software might cause bodily injury, then you should write the typedefs no matter how unlikely the error.

I can't imagine why anyone would ever want to hide a class name with a function or object name in the same scope as the class. The hiding rules in C were a mistake, and they should not have been extended to classes in C++. Indeed, you can correct the mistake, but it requires extra programming discipline and effort that should not be necessary.

Top answer
1 of 16
590

As Greg Hewgill said, the typedef means you no longer have to write struct all over the place. That not only saves keystrokes, it also can make the code cleaner since it provides a smidgen more abstraction.

Stuff like

typedef struct {
  int x, y;
} Point;

Point point_new(int x, int y)
{
  Point a;
  a.x = x;
  a.y = y;
  return a;
}

becomes cleaner when you don't need to see the "struct" keyword all over the place, it looks more as if there really is a type called "Point" in your language. Which, after the typedef, is the case I guess.

Also note that while your example (and mine) omitted naming the struct itself, actually naming it is also useful for when you want to provide an opaque type. Then you'd have code like this in the header, for instance:

typedef struct Point Point;

Point * point_new(int x, int y);

and then provide the struct definition in the implementation file:

struct Point
{
  int x, y;
};

Point * point_new(int x, int y)
{
  Point *p;
  if((p = malloc(sizeof *p)) != NULL)
  {
    p->x = x;
    p->y = y;
  }
  return p;
}

In this latter case, you cannot return the Point by value, since its definition is hidden from users of the header file. This is a technique used widely in GTK+, for instance.

UPDATE Note that there are also highly-regarded C projects where this use of typedef to hide struct is considered a bad idea, the Linux kernel is probably the most well-known such project. See Chapter 5 of The Linux Kernel CodingStyle document for Linus' angry words. :) My point is that the "should" in the question is perhaps not set in stone, after all.

2 of 16
263

It's amazing how many people get this wrong. PLEASE don't typedef structs in C, it needlessly pollutes the global namespace which is typically very polluted already in large C programs.

Also, typedef'd structs without a tag name are a major cause of needless imposition of ordering relationships among header files.

Consider:

#ifndef FOO_H
#define FOO_H 1

#define FOO_DEF (0xDEADBABE)

struct bar; /* forward declaration, defined in bar.h*/

struct foo {
  struct bar *bar;
};

#endif

With such a definition, not using typedefs, it is possible for a compiland unit to include foo.h to get at the FOO_DEF definition. If it doesn't attempt to dereference the 'bar' member of the foo struct then there will be no need to include the "bar.h" file.

Also, since the namespaces are different between the tag names and the member names, it is possible to write very readable code such as:

struct foo *foo;

printf("foo->bar = %p", foo->bar);

Since the namespaces are separate, there is no conflict in naming variables coincident with their struct tag name.

If I have to maintain your code, I will remove your typedef'd structs.

Top answer
1 of 6
184

There are several things going on here. First, as others have said, the compiler's complaint about unknown type may be because you need to declare the types before using them. More important though is to understand the syntax of 3 things:

  1. definition of struct type,
  2. definition and declaration of struct variable, and
  3. typedef

(Note that in the C-programming language, definition and declaration usually happen at the same time, and thus are essentially the same. This is not the case in many other languages. See footnote below for further details.)

When defining a struct, the struct can be tagged (named), or untagged (if untagged, then the struct must be used immediately (will explain what this means further below)).

struct Name {
   ...
};

This defines a type called "struct Name" which then can be used to define a struct variable/instance:

struct Name myNameStruct;

This defines a variable called myNameStruct which is a struct of type struct Name.

You can also define a struct, and declare/define a struct variable at the same time:

struct Name {
   ...
} myNameStruct;

As before, this defines a variable called myNameStruct which is an instance of type struct Name ... But it does it at the same time it defines the type struct Name.
The type can then be used again to declare and define another variable:

struct Name myOtherNameStruct;

Now typedef is just a way to alias a type with a specific name:

typedef OldTypeName NewTypeName;

Given the above typedef, any time you use NewTypeName it is the same as using OldTypeName. In the C programming language this is particularly useful with structs, because it gives you the ability to leave off the word "struct" when declaring and defining variables of that type and to treat the struct's name simply as a type on its own (as we do in C++). Here is an example that first defines the struct, and then typedefs the struct:

struct Name {
   ...
};

typedef struct Name Name_t;

In the above OldTypeName is struct Name and NewTypeName is Name_t. So now, to define a variable of type struct Name, instead of writing:

struct Name myNameStruct;

I can simple write:

Name_t myNameStruct;

NOTE ALSO, the typedef CAN BE COMBINED with the struct definition, and this is what you are doing in your code:

typedef struct {
   ...
} Name_t;

This can also be done while tagging (naming) the struct. This is useful for self-referential structs (for example linked-list nodes), but is otherwise superfluous. None-the-less, many follow the practice of always tagging structs, as in this example:

typedef struct Name {
   ...
} Name_t;

NOTE WELL: In the syntax above, since you have started with "typedef" then the whole statement is a typedef statement, in which the OldTypeName happens to be a struct definition. Therefore the compiler interprets the name coming after the right curly brace } as the NewTypeName ... it is NOT the variable name (as it would be in the syntax without typedef, in which case you would be defining the struct and declaring/defining a struct variable at the same time).

Furthermore, if you state typedef, but leave off the Name_t at then end, then you have effectively created an INCOMPLETE typedef statement, because the compiler considers everything within "struct Name { ... }" as OldTypeName, and you are not providing a NewTypeName for the typedef. This is why the compiler is not happy with the code as you have written it (although the compiler's messages are rather cryptic because it's not quite sure what you did wrong).

Now, as I noted above, if you do not tag (name) the struct type at the time you define it, then you must use it immediately, either to define a variable:

struct {
   ...
} myNameStruct;  // defines myNameStruct as a variable with this struct
                 // definition, but the struct definition cannot be re-used.

Or you can use an untagged struct type inside a typedef:

typedef struct {
   ...
} Name_t;

This final syntax is what you actually did when you wrote:

typedef struct{
   char firstName[56];
   char lastName[56];
} Author;

And the compiler was happy. HTH.

Regarding the comment/question about the _t suffix:

_t suffix is a convention, to indicate to people reading the code that the symbolic name with the _t is a Type name (as opposed to a variable name). The compiler does not parse, nor is it aware of, the _t.

The C89, and particularly the C99, standard libraries defined many types AND CHOSE TO USE the _t for the names of those types. For example C89 standard defines wchar_t, off_t, ptrdiff_t. The C99 standard defines a lot of extra types, such as uintptr_t, intmax_t, int8_t, uint_least16_t, uint_fast32_t, etc. But _t is not reserved, nor specially parsed, nor noticed by the compiler, it is merely a convention that is good to follow when you are defining new types (via typedef) in C. In C++ many people use the convention to start type names with an uppercase, for example, MyNewType ( as opposed to the C convention my_new_type_t ). HTH


Footnote about the differences between declaring and defining: First a special thanks to @CJM for suggesting clarifying edits, particularly in relation to the use of these terms. The following items are typically declared and defined: types, variables, and functions.

  • Declaring gives the compiler only a symbolic name and a "type" for that symbolic name.
    • For example, declaring a variable tells the compiler the name of that variable, and its type.
  • Defining gives the complier the full details of an item:
    • In the case of a type, defining gives the compiler both a name, and the detailed structure for that type.
    • In the case of a variable, defining tells the compiler to allocate memory (where and how much) to create an instance of that variable.

Generally speaking, in a program made up of multiple files, the variables, types and functions may be declared in many files, but each may have only one definition.

In many programming languages (for example C++) declaration and definition are easily separated. This permits "forward declaration" of types, variables, and functions, which can allow files to compile without the need for these items to be defined until later. In the C programming language however declaration and definition of variables are one and the same. (The only exception, that I know of, in the C programming language, is the use of keyword extern to allow a variable to be declared without being defined.) It is for this reason that in a previous edit of this answer I referred to "definition of structs" and "declaration of struct [variables]," where the meaning of "declaration of a struct [variable]" was understood to be creating an instance (variable) of that struct.

2 of 6
7

The syntax is of typedef is as follow:

typedef old_type new_type

In your first try, you defined the struct Book type and not Book. In other word, your data type is called struct Book and not Book.

In the second form, you used the right syntax of typedef, so the compiler recognizes the type called Book.

Find elsewhere
Top answer
1 of 2
15

What this does:

typedef struct {
    int a;
    int b;
} ab_t;

Is define an anonymous struct and give it the alias ab_t. For this case there's no problem as you can always use the alias. It would however be a problem if one of the members was a pointer to this type.

If for example you tried to do something like this:

typedef struct {
    int count;
    TNODE *left, *right;
} TNODE;

This wouldn't work because the type TNODE is not yet defined at the point it is used, and you can't use the tag of the struct (i.e. the name that comes after the struct keyword) because it doesn't have one.

2 of 2
7

The difference between these two typedef declarations

typedef struct tnode TNODE;

struct tnode {
    int count;
    TNODE *left, *right;
};

TNODE s, *sp;

and

typedef struct {
    int a;
    int b;
} ab_t;

. is that in the second case you declared an unnamed structure. It means that within the structure you can not refer to itself. For example you can not write

typede struct {
    int count;
    TNODE *left, *right;
} TNODE;

because the name TNODE used in this member declaration

    TNODE *left, *right;

is not declared yet.

But you can refer to the structure if the structure tag will have a name like

struct tnode {
    int count;
    struct tnode *left, *right;
};

because the name struct tnode was already declared.

Another difference is that to declare a pointer to a structure there is no need to have a complete definition of the structure. That is you may write

typedef struct tnode TNODE;

TNODE *sp;

struct tnode {
    int count;
    TNODE *left, *right;
};

Pay attention to that you may write a typedef declaration also the following way

struct tnode {
    int count;
    struct tnode *left, *right;
} typedef TNODE;
🌐
Delft Stack
delftstack.com › home › howto › struct and typedef struct in c
Difference Between Struct and Typedef Struct in C | Delft Stack
October 12, 2023 - The typedef keyword creates a brand-new name to an already existing data type but does not create the new data type. We can have a cleaner and more readable code if we use the typedef struct, and it also saves us (the programmer) from keystrokes.
Top answer
1 of 6
7

Structs seem to be exclusively used with typedefs.

This is a mistaken impression. Structs are frequently used without typedefs, and I personally prefer that. There are numerous struct types declared and used, without built-in typedefs, by the C standard library and POSIX standard library extension functions, for example.

It seems like the default behavior of defining a struct should also define a typedef.

In C++, it effectively does.

Why would I ever want to define struct without also using a typedef?

Why would you ever want to define a struct with a typedef? Using a (tagged) structure type via the struct keyword and its tag clarifies what kind of type it is, and enables you to determine quickly by eye whether two types are the same. On the other hand, a typedefed alias can represent any type at all, and there can be multiple such aliases for the same type.

There are some good and appropriate uses of typedef, but there are a lot of other uses whose propriety is a code style consideration. Myself, I strongly prefer styles that minimize use of typedef.

2 of 6
5

It's a matter of personal preference, possibly imposed onto other people working on the same project as a convention. For instance, the Linux kernel coding style guide discourages the introduction of new typedefs in no uncertain terms.

Though I don't necessarily agree with everything in that guide, some of which seems silly (for instance vps_t a; example could be virtual_container_t a;: the issue hinges on the cryptic name that is chosen for typedef more than the existence of the typedef), in my TXR language project, here are some raw stats:

txr$ git grep '^typedef struct' '*/*.[ch]' '*.[ch]' | wc
     25      91     839
txr$ git grep '^struct' '*/*.[ch]' '*.[ch]' | wc
    135     528    4710

Lines of code beginning with struct outnumber typedef struct lines by a factor of 5.4!

The union/tag namespace feature of C means that you can have a variable called foo in the same scope as a struct foo without a clash, which is useful. This extra namespace gives us an opportunity not to pollute the regular identifier namespace with user-defined type names, which improves hygiene.

The cost is that we have to type struct foo instead of just foo in declarations.

It makes particular sense if you have experience with languages in which class/type names do not intrude into the lexical variable namespace, like Common Lisp.

The above code base, though, compiles as C++, so that throws a bit of a monkey wrench into it. In C++, struct foo defines foo as a type, which can be referenced in the ordinary namespace.

Another reason is that there is some clarity. When we see a declaration like:

struct foo x;

we know that x is a structure, whereas

foo x;

could be anything; it could be typedef double foo. Sometimes we go for that kind of abstraction. When you want to hide how something is implemented, reach for typedef. However, typedef doesn't provide perfect abstraction.

Also, if we see:

struct foo f;
struct bar b;

we know that these are necessarily different, incompatible types. We do not know that given:

foo f;
bar b;

They could both be typedefs for the same structure or for int for all we know.

If you typedef pointer types, but the code dereferences them, it looks pretty silly:

foo x = get_foo();
char *fname = x->name; /* what? */

That kernel coding style document has this to say about the above: In general, a pointer, or a struct that has elements that can reasonably be directly accessed should never be a typedef.

Not using typedef for structures has as much to do with some of the hygiene of using the tag namespace, as it has to do with avoiding typedef as such: keeping the code explicit, and reserving typedef for situations in which we actually need a proper abstraction.

Top answer
1 of 3
3

There are actually four name-spaces in C (although this depends on a particular way of counting, and some include macro names as a fifth space, which I think is a valid way to think about them):

  • goto labels
  • tags (struct, union, and enum)
  • the actual members of a struct or union type (one per type, hence you could count this as "many" instead of "one" name space)
  • all other ("ordinary") identifiers, such as function and variable names and the names made to be synonyms for other types via typedef.

While it should (in theory) be possible to have separate spaces for struct vs union, for instance, C does not, so:

struct foo; union foo; /* ERROR */

is invalid. Yet:

struct foo { int a, b; };
struct bar { char b; double a; };

is just fine, showing that the members of the two different struct types are in different name-spaces (so again this makes the count of "4 name-spaces" above suspect :-) ).

All that aside, C has some moderately (and in some ways unnecessarily) complicated, but quite workable in practice, rules for how struct types work.

Each struct creates a new type unless it refers back to an existing type. The struct keyword may be followed by an identifier, or just an open brace {. If there is just an open brace, the struct creates a new type:

struct { ... } X; /* variable X has a unique type */

If there is an identifier, the compiler must look at the (single) tag name-space to see if that name is already defined. If not, the struct defines a new type:

struct blart { ... } X; /* variable X has type <struct newname>, a new type */

If the identifier is already present, generally this refers back to the existing type:

struct blart Y; /* variable Y has the same type as variable X */

There is one special exception, though. If you're in a new scope (such as at the beginning of a function), a "vacuous declaration"—the struct keyword, followed by an identifier, followed by a semicolon—"clears out" the previous visible type:

void func(void) {
    struct blart; /* get rid of any existing "struct blart" */
    struct blart { char *a; int b; } v;

Here v has a new type, even if struct blart was already defined outside func.

(This "vacuous declaration" trick is mostly useful in obfuscated code contests. :-) )

If you're not at a new scope, a vacuous declaration serves the purpose of declaring that the type exists. This is mainly useful to work around a different issue, which I will cover in a moment.

struct blart;

Here struct blart alerts you (and the compiler) that there is now a type named "struct blart". This type is merely declared, meaning that the struct type is "incomplete", if struct blart has not yet been defined. This type is defined (and "complete") if struct blart has been defined. So:

struct blart { double blartness; };

defines it, and then any earlier or later struct blarts refer to the same type.


Here's why this sort of declaration is useful. In C, any declaration of an identifier has scope. There are four possible scopes: "file", "block", "prototype", and "function". The last one (function scope) is exclusively for goto labels, so we can ignore it from here on. That leaves file, block, and prototype scopes. File scope is a technical term for what most people think of as "global", in contrast with "block scope" which is "local":

struct blart { double blartness } X; /* file scope */
void func(void) {
    struct slart { int i; } v; /* block scope */
    ...
}

Here struct blart has file scope (as does "global" variable X), and struct slart has block scope (as does "local" variable v).

When the block ends, struct slart goes away. You can no longer refer to it by name; a later struct slart creates a new and different type, in exactly the same way that a later int v; creates a new v, and does not refer to the v within the block scope inside function func.

Alas, the committee that designed the original C standard included (for good reason) one more scope, inside the function prototype, in a way that interacts rather badly with these rules. If you write a function prototype:

void proto(char *name, int value);

the identifiers (name and value) disappear after the closing parenthesis, just as you'd expect—you wouldn't want this to create a block-scope variable called name. Unfortunately, the same happens with struct:

void proto2(struct ziggy *stardust);

The name stardust goes away, but so does struct ziggy. If struct ziggy did not appear earlier, that new, incomplete type that is created inside the prototype, has now been removed from all human reach. It can never be completed. Good C compilers print a warning here.

The solution is to declare the struct—whether complete or not [*]—before writing the prototype:

struct ziggy; /* hey compiler: "struct ziggy" has file scope */
void proto2(struct ziggy *stardust);

This time, struct ziggy has an already-existing, visible declaration to refer back to, so it uses the existing type.

[* In header files, for instance, you often don't know if the header that defines the struct has been included, but you can declare the struct yourself, and then define protoypes that use pointers to it.]


Now, as to typedef...

The typedef keyword is syntactically a storage-class specifier, like register and auto, but it acts quite weird. It sets a flag in the compiler that says: "change variable declarations into type-name aliases".

If you write:

typedef int TX, TY[3], *TZ;

the way that you (and the compiler) can understand this is to start by removing the typedef keyword. The result needs to be syntactically valid, and it is:

int TX, TY[3], *TZ;

This would declare three variables:

  • TX has type int
  • TY has type "array 3 of int"
  • TZ has type "pointer to int"

Now you (and the compiler) put the typedef back in, and change "has" to "is another name for":

  • TX is another name for type int
  • TY is another name for "array 3 of int"
  • TZ is another name for "pointer to int"

The typedef keyword works with struct types in exactly the same way. It's the struct keyword that creates the new type; then typedef changes the variable declaration(s) from "has type ..." to "is another name for type ...". So:

typedef struct ca ca_t;

starts by either creating new type, or referring back to existing type, struct ca as usual. Then, instead of declaring a variable ca_t as having type struct ca, it declares the name as another name for the type struct ca.

If you omit the struct tag name, you are left with only two valid syntactic patterns:

typedef struct; /* note: this is pointless */

or:

typedef struct { char *top_coat; int top_hat; } zz_t, *zz_p_t;

Here, struct { creates a new type (remember, we said this way back at the beginning!), and then after the closing }, the identifiers that would have declared variables, now make type-aliases. Again, the type was actually created by the struct keyword (although it hardly matters this time; the typedef-names are now the only ways to refer to the type).

(The reason the first pointless pattern is the way it is, is that without the braces, the first identifier you stick in is the struct-tag:

typedef struct tag; /* (still pointless) */

and thus you haven't omitted the tag after all!)


As for the last question, about the syntax error, the problem here is that C is designed as a "single pass" language, where you (and the compiler) never have to look very far forward to find out what something is. When you attempt something like this:

typedef struct list {
    ...
    List *next; /* ERROR */
} List;

you've given the compiler too much to digest at once. It starts by (in effect) ignoring the typedef keyword except to set the flag that changes the way variables will be declared. This leaves you with:

struct list {
    ...
    List *next; /* ERROR */
}

The name List is simply not yet available. The attempt to use List *next; does not work. Eventually the compiler would reach the "variable declaration" (and because the flag is set, change it to a type-alias instead), but it's too late by then; the error has already occurred.

The solution is the same as with function prototypes: you need a "forward declaration". The forward declaration will give you an incomplete type, until you finish defining the struct list part, but that's OK: C lets you use incomplete types in a number of positions, including when you want to declare a pointer, and including with typedef alias-creation. So:

typedef struct list List; /* incomplete type "struct list" */

struct list { /* begin completing "struct list" */
    ...
    List *next; /* use incomplete "struct list", through the type-alias */
}; /* this "}" completes the type "struct list" */

This gains relatively little over just writing struct list everywhere (it saves a bit of typing, but so what? well, OK, some of us suffer a bit of carpal tunnel / RSI issues :-) ).


[Note: this last segment is going to cause controversy... it always does.]

In fact, if you mentally replace struct with type, C code becomes a whole lot nicer to "strongly typed language" fans. Instead of the terrible [%], weak-sauce:

typedef int distance; /* distance is measured in discrete units */
typedef double temperature; /* temperatures are fractional */

they can write:

#define TYPE struct

TYPE distance;
TYPE temperature;

These, being incomplete types, are truly opaque. To create or destroy or indeed do anything with a distance value you must call a function (and—for most variables anyway; there are some exceptions for external identifiers—use pointers, alas):

TYPE distance *x = new_distance(initial_value);

increase_distance(x, increment);
use_distance(x);
destroy_distance(x);

Nobody can write:

*x += 14; /* 3 inches in a dram, 14 ounces in a foot */

It simply won't compile.

Those who are a bit less bondage-and-discipline with their type systems can relax the constraints by completing the type:

TYPE distance { int v; };
TYPE temperature { double v; };

Of course, now "cheaters" can do:

TYPE distance x = { 0 };
x.v += 14; /* 735.5 watts in a horsepower */

(well, at least that last comment is correct).

[% Not really that terrible, I think. Some seem to disagree.]

2 of 3
2

1) The difference between those two blocks of code is that the first one is invalid syntax, while the second one is good and useful. I use the second one in order to define a struct and also define a typedef for the struct at the same time. My code has stuff that looks like this:

typedef struct Dog {
  int age, barks;
} Dog;

After that line, I can define dogs with Dog mydog; or struct Dog mydog;.

It's important to understand that the code above is doing two things. It is defining a type named struct Dog, and then it is defining a type named Dog that just refers to struct Dog. You could split that into two separate steps like this:

struct Dog {
  int age, barks;
};    
typedef struct Dog Dog;

2) I always use the typedef as shown above in the first block of code and have found no problem with it. I would say there are no advantages to leaving out the typdef. Just for the record, if you want to leave out the typedef and only define a struct, then you code would be:

struct Dog {
  int age, barks;
};

If you do it that way, you can only make new dogs by typing struct Dog mydog;; in other words, the name of the type is only struct Dog and Dog does not name a type.

3) The problem is that you are trying to use "Node" inside the definition of "Node". That would be a circular definition. You can fix everything by just writing it like this:

struct Node;
typedef struct Node
{
    struct Node * next;
    struct Node * previous;
} Node;
Top answer
1 of 2
29

Let's analyse your code a bit:

typedef struct nodes
{
    int data;
    struct node *next;
}node;

This declares and defines struct nodes, a type with two members, and declares a type alias so we can refer to it only as node.

Now, in C++, the member declaration struct node *next automatically forward-declares a type called node. That then conflicts with your typedef target node: it's as if you're trying to give two types the same name.

In C, there is no conflict, because the type called node can in fact only be referred to as struct node.

The second snippet worked because, since during parsing of the member declaration struct node already exists, no new type is forward-declared there … and since all you're then doing is renaming it in the same typedef statement, C++ doesn't really care, knowing that it's all the same type (struct T is T; the difference is in syntax, not in name).

[C++11: 7.1.3/3]: In a given non-class scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers. [ Example:

typedef struct s { / ... / } s;
typedef int I;
typedef int I;
typedef I I;

—end example ]

[C++11: 7.1.3/6]: In a given scope, a typedef specifier shall not be used to redefine the name of any type declared in that scope to refer to a different type. [ Example:

class complex { / ... / };
typedef int complex; // error: redefinition

—end example ]

Of course, in C++, this is all moot and you should just write:

struct node
{
   int data;
   node* next;
};

You do not need to typedef-away the elaborated-type-specifier struct.

2 of 2
5

The C example you gave should be an error. You're using a tag name (node) that you haven't defined with struct node.

Given those two choices, the second is the one to use. I prefer a bit of economy:

typedef struct node_t
{
    int data;
    struct node_t *next;
} node_t;

In C or C++, the tag names have their own namespace, so there is no problem with using the same name for the tag and the typedef name. In C, this allows you to use either node_t or struct node_t to refer to this struct type. C++ will search the tag names for a type name if a declared type name doesn't exist, so the above double-definition isn't needed, but doesn't hurt.

In both languages, the explicit struct node_t version is required at any point before the type is completely defined, so any self-reference, and any forward references will use the struct version. I prefer this in header files, mostly because it reduces problems with order of #include directives.

PS: This does work in either language (see LRIO's answer for pointers into the C++11 Standard) and has been used in enough bilingual and even pure C++ header files that it's unlikely to disappear soon) so it's a very simple approach that just works in either language.

🌐
w3resource
w3resource.com › c-programming-exercises › c-snippets › difference-between-typedef-struct-and-struct-definitions-with-example.php
C - Difference between typedef struct and struct
November 1, 2025 - In C language, struct is used to define a user-defined data type that groups together variables of different data types under a single name. A typedef can be used to create an alias for an existing data type, including a struct.