Purpose of Struct, Typedef Struct, in C++

Why should we typedef a struct so often in C?

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.

typedef struct vs struct definitions

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;
}

How does the typedef struct work syntactically?

The typedef statement allows you to make an alias for the given type. You can then use either the alias or the original type name to declare a variable of that type.

typedef int number;

This creates an alias for int called number. So the following two statements declare variables of the same type:

int num1;
number num2;

Now using your example:

typedef struct {
char pseudo[MAX_SIZE] ;
int nb_mar ;
} player ;

This creates an anonymous struct and gives it the alias player. Because the struct doesn't have name, you can only use the alias to create a variable of that type.

struct set_of_players {
player T[NB_MAX_JOUEURS] ;
int nb ;
};

This creates a struct named struct set_of_players, but does not declare a typedef nor does it declare a variable of that type. Also notice that one of the fields is an array of type player, which we defined earlier. You can later declare a variable of this type as follows:

struct set_of_players my_set_var;

This line:

typedef struct set_of_players players;

Creates an alias for struct set_of_players called players. In this case the struct is not defined at the same time as the typedef but is defined elsewhere. So now you can declare a variable of this type like this:

players my_set_var;

Which is the same as the version using the full struct name.

How to properly use `typedef` for structs in C?

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.

What is the real difference between struct and typedef struct in C?

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.

When does it make sense to use a struct without a typedef?

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.

Purpose of struct, typedef struct, in C++

The typedef version is a special case of

typedef foo bar;

which defines a new "type" bar as an alias for foo. In your case, foo happens to be a struct. In C, this was the only way to introduce new "types" (in quotes, because they are not really equivalent to int, float and co). In C++, this is not so useful, because C++ was designed to make definition of new types easier and more complete than C (at least at the beginnings of C++), and the typedef is not even necessary to refer to a previously declared struct (or class).

Difference between 'struct' and 'typedef struct' in C++?

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.

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

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.

Usage of Typedef Struct in C programming

The template you were given is this, where I've added numbers to some of the lines for ease of reference:

typedef struct NODE_s *NODE;    // 1
typedef struct NODE_s // 2
{
NODE right; // 3
NODE left;
unsigned long data;
int height;
} NODE_t[1]; // 4

typedef struct TREE_s *TREE;
typedef struct TREE_s
{
NODE root;
} TREE_t[1];

TREE tree_init(); // 5
NODE node_init(unsigned long data);

What are the problems here?

  1. As noted in comments, the SO Q&A Is it a good idea to typedef pointers suggests that it is not a good idea to typedef pointers, with limited exceptions for 'pointers to functions' (not relevant here) and perhaps (but probably not) for opaque types. This line does two things: (1) it says "there is a structure type with the tag NODE_s; (2) the name NODE is a synonym for struct NODE_s *. The structure type is incomplete at the moment; no details are known about its members.
  2. This line starts a new typedef, but also starts the definition of the type struct NODE_s (because of the { that follows on the next line).
  3. The type NODE is already known; it can be used here. It means the same as if you wrote struct NODE_s *right;.
  4. The name NODE_t is an alias for the type struct NODE_s[1], an array of 1 struct NODE_s. It isn't clear what this is going to be used for. I have reservations (at best) about its existence. (You can also apply the discussion in points 1, 2, 4 to the struct TREE_s type, mutatis mutandis.)
  5. This is a function declaration, but it is not a prototype declaration. It says that the tree_init() function can be called with any number of arguments of any type because no information is specified about the number or type of those arguments. We do know it is not a variadic function (variable argument list, like printf()) because those must have a full prototype declaration ending with , ...) in scope before they're used. If you want to specify that the function takes no arguments, say so: TREE tree_init(void);.

I think the intent behind the NODE_t and TREE_t types is to allow you to write, for example:

int main(void)
{
TREE_t x = { 0 };

if (x->root != 0)
return 1;
return 0;
}

I'm not convinced whether that's sufficiently helpful to warrant the type compared with using struct NODE_s x and using x.root in the test. It does save you from having to add an & when passing a pointer to a function; again, I'm not sure it is really sufficiently helpful to warrant its existence.

What I would prefer to see as the template is:

typedef struct NODE_s NODE;
struct NODE_s
{
NODE *right;
NODE *left;
unsigned long data;
int height;
};

typedef struct TREE_s TREE;
struct TREE_s
{
NODE *root;
};

extern TREE *tree_init(void);
extern NODE *node_init(unsigned long data);

This removes the pointers from the typedef statements, avoids the somewhat peculiar array types, and uses an explicit prototype for tree_init(). I personally prefer to have function declarations marked with extern; in a header, they'll match the extern on those rare global variables that are declared in the header. Many people prefer not to use extern because the compiler assumes that anyway — so be it; the most important thing is consistency.

The code in main() would now be written:

int main(void)
{
TREE x = { 0 };

if (x.root != 0)
return 1;
return 0;
}

The difference? An arrow -> changed to a dot .. Not a lot of problem there. However, when calling functions, you'd probably use &x whereas with the TREE_t type, you would just write x (because it's an array).



Related Topics



Leave a reply



Submit