What is 'forward declaration' and the difference between 'typedef struct X' and 'struct X'?

CStructTypedef

C Problem Overview


I am a beginner in C programming and I know the difference between struct type declaration and typedef struct declaration. I came across to know an answer saying that if we define a struct like:

typedef struct { 
    some members;
} struct_name;

Then it will be like providing an alias to an anonymous struct (as it is not having a tag name). So it can't be used for forward declaration. I don't know what the forward declaration means.

Also, I wanted to know that for the following code:

typedef struct NAME { 
    some members;
} struct_alias;

Is there any difference between NAME and struct_alias? Or are both equal as struct_alias is an alias of struct NAME ?

Furthermore, can we declare a variable of type struct NAME like these:

struct_alias variable1;

and/or like:

struct NAME variable2;

or like:

NAME variable3; 

C Solutions


Solution 1 - C

struct forward declarations can be useful when you need to have looping struct declarations. Example:

struct a {
	struct b * b_pointer;
	int c;
};

struct b {
	struct a * a_pointer;
	void * d;
};

When struct a is declared it doesn't know the specs of struct b yet, but you can forward reference it.

When you typedef an anonymous struct then the compiler won't allow you to use it's name before the typedef.

This is illegal:

struct a {
	b * b_pointer;
	int c;
};

typedef struct {
	struct a * a_pointer;
	void * d;
} b;

// struct b was never declared or defined

This though is legal:

struct a {
	struct b * b_pointer;
	int c;
};

typedef struct b {
	struct a * a_pointer;
	void * d;
} b;

// struct b is defined and has an alias type called b

So is this:

typedef struct b b;
// the type b referes to a yet undefined type struct b

struct a {
	b * struct_b_pointer;
	int c;
};

struct b {
	struct a * a_pointer;
	void * d;
};

And this (only in C, illegal in C++):

typedef int b;

struct a {
	struct b * struct_b_pointer;
	b b_integer_type;
	int c;
};

struct b {
	struct a * a_pointer;
	void * d;
};

// struct b and b are two different types all together. Note: this is not allowed in C++

Solution 2 - C

Forward declaration is a promise to define something that you make to a compiler at the point where the definition cannot be made. The compiler can use your word to interpret other declarations that it would not be able to interpret otherwise.

A common example is a struct designed to be a node in a linked list: you need to put a pointer to a node into the struct, but the compiler would not let you do it without either a forward declaration or a tag:

// Forward declaration
struct element;
typedef struct {
	int value;
    // Use of the forward declaration
	struct element *next;
} element; // Complete definition

> and so it cant be used for forward declaration

I think that author's point was that giving your struct a tag would be equivalent to a forward declaration:

typedef struct element {
	int value;
    // No need for a forward declaration here
	struct element *next;
} element;

Solution 3 - C

Forward declaration is a declaration preceeding an actual definition, usually for the purpose of being able to reference the declared type when the definition is not available. Of course, not everything may be done with the declared-not-defined structure, but in certain context it is possible to use it. Such type is called incomplete, and there are a number of restrictions on its usage. For example:

struct X; // forward declaration

void f(struct X*) { }  // usage of the declared, undefined structure

// void f(struct X) { }         // ILLEGAL
// struct X x;                  // ILLEGAL
// int n =sizeof(struct X);     // ILLEGAL

// later, or somewhere else altogether
struct X { /* ... */ };

This can be useful e.g. to break circular dependencies, or cut down the compilation time, as the definitions are usually significantly larger, and so more resources are required to parse it.

In your example, struct NAME and struct_alias are indeed equivalent.

struct_alias variable1;
struct NAME variable2;

are correct;

NAME variable3;

is not, as in C the struct keyword is required.

Solution 4 - C

struct_alias and struct NAME are same ,struct_alias is an alias to struct NAME

These both are same and allowed

struct_alias variable1;  

struct NAME variable1; 

this is illegal

NAME variable3;   

See this article on Forward declaration

Solution 5 - C

As others stated before, a forward declaration in C/C++ is the declaration of something with the actual definition unavailable. Its a declaration telling the compiler "there is a data type ABC".

Lets pretend this is a header for some key/value store my_dict.h :

...
struct my_dict_t;
struct my_dict_t* create();

char* get_value(const struct my_dict_t* dict, const char* name);
char* insert(struct my_dict_t* dict, const char* name, char* value);
void destroy(struct my_dict_t* dict);
...

You dont know anything about my_dict_t, but actually, for using the store you dont need to know:

#include "my_dict.h"
...
struct my_dict_t* dict = create();
if(0 != insert(dict, "AnEntry", strdup("AValue"))) {
    ...
}
...

The reason for this is: You are only using POINTERS to the data structure.

POINTERS are just numbers, and for dealing with them you dont need to know what they are pointing at.

This will only matter if you try to actually access them, like

struct my_dict_t* dict = create();
printf("%s\n", dict->value);  /* Impossible if only a forward decl is available */

So, for implementing the functions, you require an actual definition of my_struct_t. You might do this in the source file my_dict.c like so:

#include "my_dict.h"

struct my_dict_t {
    char* value;
    const char* name;
    struct my_dict_t* next;
}

struct my_dict_t* create() {
    return calloc(1, sizeof(struct my_dict_t));
}

This is handy for several situations, like

  • For resolving circular type dependencies, like Sergei L. explained.
  • For encapsulation, like in the example above.

So the question that remains is: Why cant we just omit the forward declaration at all when using the functions above? In the end, it would suffice for the compiler to know that all dict are pointers.

However, the compiler does perform type checks: It needs to verify that you don't do something like

...
int i = 12;
char* value = get_value(&i, "MyName");
...

It does not need to know how my_dict_t looks like, but it needs to know that &i is not the type of pointer get_value() expects.

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
Questionr_goyalView Question on Stackoverflow
Solution 1 - CSergey L.View Answer on Stackoverflow
Solution 2 - CSergey KalinichenkoView Answer on Stackoverflow
Solution 3 - CMarcin ŁośView Answer on Stackoverflow
Solution 4 - CGangadharView Answer on Stackoverflow
Solution 5 - CMichael BeerView Answer on Stackoverflow