C : typedef struct name {...}; VS typedef struct{...} name;

CStructTypedef

C Problem Overview


As the title says, I have this code:

    typedef struct Book{
        int id;
        char title[256];
        char summary[2048];
        int numberOfAuthors;
        struct Author *authors;
    };


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


    typedef struct Books{
        struct Book *arr;
        int numberOfBooks;
    };

I get these errors from gcc :

bookstore.c:8:2: error: unknown type name ‘Author’
bookstore.c:9:1: warning: useless storage class specifier in empty declaration [enabled by default]
bookstore.c:15:1: warning: useless storage class specifier in empty declaration [enabled by default]
bookstore.c:21:2: error: unknown type name ‘Book’
bookstore.c:23:1: warning: useless storage class specifier in empty declaration [enabled by default]

No warnings and no errors occur if I change the typedefs like this:

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

Having searched through C Programming Language, 2nd Edition and googled for a couple of hours, I can't figure out why the first implementation won't work.

C Solutions


Solution 1 - C

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.

Solution 2 - C

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.

Solution 3 - C

Want to add by clarifying when you actually declare a variable.

struct foo {
   int a;
} my_foo;

defines foo and immediately declares a variable my_foo of the struct foo type, meaning you can use it like this my_foo.a = 5;

However, because typedef syntax follows typedef <oldname> <newname>

typedef struct bar {
   int b;
} my_bar;

is not declaring a variable my_bar of type struct bar, my_bar.b = 5; is illegal. It is instead giving a new name to the struct bar type in the form of my_bar. You can now declare the struct bar type with my_bar like this:

my_bar some_bar;

Solution 4 - C

The other answers are all correct and useful, but maybe longer that necessary. Do this:

typedef struct Book Book;
typedef struct Books Books;
typedef struct Author Author;

struct Book {
    ... as you wish ...
};

struct Author {
    ... as you wish ...
};

struct Books {
    ... as you wish ...
};

You can define the your struct's in any order provided they only contain pointers to other struct's.

Solution 5 - C

You just need to define Author before defining Book.

You use Author in Book so it needs to be defined before.

Solution 6 - C

I think is going to help you understand. http://www.tutorialspoint.com/cprogramming/c_typedef.htm

bookstore.c:8:2: error: unknown type name ‘Author’
bookstore.c:21:2: error: unknown type name ‘Book’

These are produced because you have to define them before you use them. Move the struct "Author" & "Books" above the struct "Book". This will solve it.

Also the warning you are getting explains why there is a problem, the compiler identifies "typedef struct Author" as not necessary because you are not properly typedef the struct so there is nothing useful for the compiler to "read".

Since you already know the answer should be in this form

typedef struct {
 ...
 ... 
 ...
} struct-name;

stick with that.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlek SobczykView Question on Stackoverflow
Solution 1 - CDaniel GoldfarbView Answer on Stackoverflow
Solution 2 - CBechirView Answer on Stackoverflow
Solution 3 - CAndrew CinaView Answer on Stackoverflow
Solution 4 - CLorinczy ZsigmondView Answer on Stackoverflow
Solution 5 - CEoiFirstView Answer on Stackoverflow
Solution 6 - Caiked0View Answer on Stackoverflow