71

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.

Trevor Reid
  • 3,310
  • 4
  • 27
  • 46
Alek Sobczyk
  • 813
  • 1
  • 7
  • 6
  • 9
    move the `Author` before `Book`. Also note that your `typedef`s are redundant – SomeWittyUsername Jul 18 '13 at 09:59
  • How is it possible that just change in `Author` structure is removing `error:unknown type name ‘Book’` also? Please have a look [here](http://msdn.microsoft.com/en-us/library/4x7sfztk%28v=vs.80%29.aspx) which clearly mention difference between typdef a structure and just defining a structure. – Dayal rai Jul 18 '13 at 10:16

6 Answers6

171

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.

Daniel Goldfarb
  • 6,937
  • 5
  • 29
  • 61
  • 2
    can we declare any type with the `_t` suffix? I thought this suffix is C99 reserved. Am I wrong? – nowox Mar 04 '15 at 11:48
  • 8
    It is important to note that self-referential structs require a name for the struct, or a forward-declared typedef. I prefer for instance if I want a typed LinkedList node to write: `typedef struct node_t {void * data; struct node_t next; } Node;` so that the declaration is complete and concise. – aaroncarsonart Dec 29 '15 at 15:59
  • 6
    Seriously, this solved all my confusion about C struct. Thank you! – slow Jan 30 '19 at 22:40
  • Question about the statement "`if you do not name the struct type at the time you define it, then you must use it immediately either to declare a variable`": the `myNameStruct` variable, as declared in the answer, is a variable of an anonymous struct type. Because the struct type is unnamed, you can't declare another such variable. So there can only ever be one instance of that type right? And the only pointer type that will point to an anonymous struct is `void *`? – MTV Feb 25 '20 at 19:55
  • I just realized that you need to cast `void *` to a suitable type before you're able to dereference and access the struct's members. Since it's not possible to know the type, does this mean you can't have any pointer that points to an anonymous struct variable? – MTV Feb 25 '20 at 20:08
  • 2
    @MTV "`Because the struct type is unnamed, you can't declare another such variable. So there can only ever be one instance of that type right?`" That's correct. That is why using an anonymous struct is something you would rarely do. The only time I have ever done such a thing is if I only temporarily need a local struct inside a function. Certainly if you need to pass the data anywhere else you would name the struct, or more commonly typedef it. And if multiple files need access to the struct definition you would put it in a header file. Hope that helps. – Daniel Goldfarb Feb 26 '20 at 21:26
  • 1
    The `Name` in `struct Name {};` is called structure's tag. Compiler uses this tag to find the definition. This is important, because if your struct refers to itself (for example in a linked list) then you **must** use a tagged struct otherwise, the typedef declaration is not finished yet and the type name is unknown to compiler. AFAIK this is the only distinction between these two ways of defining structs. – artronics May 05 '20 at 20:25
  • @SeyedJalalHosseini ... the exact same comment was made above by AaronCarson. It's basically the difference between something that is named (aka 'tagged') and something that is anonymous. If anonymous, it may be used only once, which includes **not** being able to self-reference the definition. Therefore, for self-referential structs, it either must be named (tagged), or, as AaronCarson points out, as an alternative to naming or tagging, a forward-declaration of the typedef also works for self-referential structs. – Daniel Goldfarb May 06 '20 at 14:32
  • For the record though, if defining a self-referential struct **in C**, naming/tagging is much easier than a struct forward declaration. – Daniel Goldfarb May 06 '20 at 14:47
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.

Bechir
  • 987
  • 10
  • 26
  • That is only the syntax for simple cases, a counterxample would be `typedef int x[5];`, or `typedef int (*p)();` – M.M Nov 07 '19 at 20:35
2

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;
Andrew Cina
  • 134
  • 10
1

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.

Lorinczy Zsigmond
  • 1,749
  • 1
  • 14
  • 21
0

You just need to define Author before defining Book.

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

Yabada
  • 1,728
  • 1
  • 15
  • 36
  • Thanks for the quick responses. There is no error defining Book before Author, checked it in Kernighan and Ritchie's book. – Alek Sobczyk Jul 18 '13 at 10:19
  • I was wrong, apparently it does remove the errors if i change positions. I have to study a bit more. (Sorry for double comment, first timer in stackoverflow :P) – Alek Sobczyk Jul 18 '13 at 10:28
0

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.

aiked0
  • 38
  • 5