I am try to understand this C code:
typedef struct _IntElem *IntList;
typedef struct _IntElem { int head; IntList tail;} IntElem;
I understand that it defines a recursive type, a list. However I don't understand the way it is declared. What I understand is the second line, we define a structure named IntElem which consists of an integer and an IntList. But what is the _IntElem in this declaration?
The first line allocates memory for the list?
The first line
typedef struct _IntElem *IntList;
is to just create a typedef or an alias for struct _IntElem *. The alias is named IntList. FWIW, at this point of time, the definition of struct _IntElem need not to be known to the compiler.
The second line
typedef struct _IntElem { int head; IntList tail;} IntElem;
Actually defines an user-defined type _IntElem, with two members, head as int and tail as IntList(typedefed earlier)
typedef the type to IntElem.
Please Note: There is actually no variable created, for either of the type (s) (I'm not talking about the member variables, of course, they are part of the definition). So, there is no memory allocated , as such.
So, to be explicit
But what is the _IntElem in this declaration?
the _IntElem is the name of the user-defined data type. However, in some cases, it is optional. for example,
typedef struct { char name [32]; int age; float salary;} employee;
is both perfectly fine and legal, except the fact, in this case, we're creating an alias employee to an unnamed data type.
In C, if we have:
struct X
{
stuff....
};
then X is called a struct tag. It means that struct X is the name of a type. X on its own is not the name of a type. In fact X on its own behaves like any other undeclared identifier; this struct definition only defines the specific usage struct X.
We might say that struct tags live in a different namespace to other identifiers (not to be confused with C++ namespaces!)
Sometimes people don't like typing struct X everywhere. So they use typedef (which, confusingly, means to create a type alias) in order to make a single identifier that names the type:
typedef struct X SX;
means that SX is a type. But since struct tags are in their own namespace, you could even write:
typedef struct X X;
which means that X on its own refers to the same type as struct X does.
Hopefully you understand the code you are looking at now. I think it would have been clearer to write:
struct IntElem
{
int head;
struct IntElem *tail;
};
with the optional addition to save typing:
typedef struct IntElem IntElem;
Pointer typedefs are evil, so IntList can be omitted entirely. Possibly you could keep it around to use as an opaque type for the users of your list, but you should avoid using it during your list implementation.
Related
While researching queues in C, I came across an example similar to the below. Why is the struct named both at the beginning of the curly braces and after? Why is struct type used again inside of the struct when adding an item of the same type? Are these things redundant or is there a point?
typedef void* vpoint_t;
typedef struct queue_item_t{
vpoint_t void_item;
struct queue_item_t* next;
} queue_item_t;
typedef struct queue_item_t { // 1
vpoint_t void_item;
struct queue_item_t* next; // 2
} queue_item_t; // 3
First of all, note that this entire statement is defining a typedef.
3) Is saying that the new type we are in the process of defining (via typedef) is going to be named queue_item_t.
1) The name of the structure (which is being given a new name, as we go), is named struct queue_item_t. That's it's full name, including struct at the front.
2) Because the new type doesn't yet exist (remember, we're still in the process of defining it), we have to use the only name it has thus far, which is struct queue_item_t, from 1).
Note that you can have anonymous struct definitions, which allow you to omit the name from 1). A simple example:
typedef struct {
int x, y, z;
} vector3;
In your example however, since we need the structure to be able to refer to itself, the next pointer must have a type that's already defined. We can do that by forward declaring the struct, typedefing it, then defining the struct using the typedefd type for next:
struct _queue_item; // 4
typedef struct _queue_item queue_item_t; // 5
struct _queue_item { // 6
vpoint_t void_item;
queue_item_t* next; // 7
}
4) Declare that struct _queue_item exists, but don't yet provide a definition for it.
5) Typedef queue_item_t to be the same as struct _queue_item.
6) Give the definition of the structure now...
7) ...using our typedef'd queue_item_t.
All that being said... In my opinion, please don't use typedefs for structs.
struct queue_item {
void *data;
struct queue_item *next;
}
is simple and complete. You can manage to type those six extra characters.
From the Linux Kernel coding style:
Chapter 5: Typedefs
Please don't use things like "vps_t".
It's a mistake to use typedef for structures and pointers. When you see a
vps_t a;
in the source, what does it mean?
In contrast, if it says
struct virtual_container *a;
you can actually tell what "a" is.
There are exceptions, which you can read about.
Some recent related questions:
Why use an opaque “handle” that requires casting in a public API rather than a typesafe struct pointer?
Does casting a pointer back and forth from size_t or uintptr_t break strict aliasing?
Let's change the declaration a little bit to make the discussion easier to follow:
typedef struct queue_item {
vpoint_t void_item;
struct queue_item* next;
} QueueItemType;
C supports several different name spaces; one name space is reserved for tag names on unions, structures, and enumeration types. In this case, the tag name is queue_item. Another name space is reserved for regular identifers, including typedef names like QueueItemType.
The next member is being used to point to another instance of type struct queue_item (i.e., the next item in the queue). It's declared as a pointer to struct queue_item for two reasons:
A struct type cannot contain an instance of itself; for one thing, the type would have to be infinitely large (struct queue_item contains a member next, which is a struct queue_item that contains a member next, which is a struct queue_item that contains a member next, ad infinitum);
The struct type definition isn't complete until the closing }, and you can't declare an instance of an incomplete type. However, you can declare a pointer to an incomplete type, which we do below:
struct queue_item *next;
Why not use QueueItemType *next; instead of struct queue_item *next? Again, the struct type definition isn't complete at the point next is being declared; the typedef name QueueItemType doesn't exist yet. However, the tag name queue_item is already visible to the compiler, so we can declare pointers using the type struct queue_item.
Since tag names and typedef names occupy different name spaces, it's possible to use the same name for both the tag name and the typedef name without a collision. The compiler disambiguates between the two by the presence of the struct keyword.
First, I suggest to NEVER use a struct name and identical type name. Use typedef struct QUEUE_ITEM {...} queue_item_t;
As to the question: if you want to make a "recursive data structure", that is, a data structure that has pointers to instances of itself, then you must be able to tell the compiler "This field is a pointer to one of ourselves. You don't know what we look like yet completely, because I am still defineing it, so just reserve space for a pointer". To do that you declare
struct T {
...
struct T *ptr;
....
};
With the final } queue_item_t; you create a new name for the structure.
"struct foo {...} " is one thing. It defines a struct, you need to type "struct foo" in every place you use it.
"typedef ... foo" defines a new type, so you just type "foo" where you use it.
"typedef struct foo {...} foo" is an idiom so you can use both, most probably just "foo" to save keystrokes and visual pollution.
We all know how to declare a structure in C:
struct Label1{ /* variables */ } Label2; // As I learned
But I want to know why this code works without declaring 'struct name':
typedef struct name s_name;
Or is in fact, does typing the code
struct name;
mean that I declared 'struct name' as a void structure or something like this?
Example of code:
typedef struct Data Data;
struct Data{ /*variables*/ };
If in the first line struct Data is declared as a void one, then in the second it's like I'm redeclaring it with members.
What is the explanation for this point?
Something like:
struct MyStruct;
Is called a forward reference. It creates an incomplete type and tells the compiler there will be a type of that name (and it's a struct - it works likewise for unions), and the details "follow later". Of such a type you cannot define variables, until you complete the type.
typedef struct MyStruct MyType;
Will just define the type name to be that struct. This is still an incomplete type.
However, you can take a pointer to an incomplete type:
MyType *my_t_pointer;
struct MyStruct *my_s_pointer;
This is useful for a struct to have pointers to objects of the same type when you provide the full declaration, "completing" the type:
struct MyStruct {
struct MyStruct *next;
};
Actually this is the only way to create nodes for lists, trees, and all other recursive data-structures. This is a major part of C programs (sometimes hidden).
Also, this mechanism is used to hide implementation details. Functions in the header need only know the struct exists to take/pass pointers to it. The use of these functions need not to know the details of the struct (but this way it cannot allocate it, so the module has to cover all aspects which need to know details on the struct). The full declaration is only inside the implementation file of the module.
These pointers are called "opaque" as one cannot "look through", i.e. access the fields of the struct as they are simply not known to it.
my_module.h:
struct MyStruct;
extern void my_init(struct MyStruct *obj);
my_module.c:
struct MyStruct {
int f1;
...
};
my_init(struct MyStruct *obj)
{
...
}
The typedef declares s_name as an alias for struct name so that you can declare variables, e.g.:
s_name *sptr;
The line
struct name;
declares that there is a struct type called name without defining its content. This is usually done in order to be able to declare variables as pointers to the struct type. You cannot declare variables of the actual struct type until it has been defined.
My understanding of C is that there are two separate namespaces, one for tags (such as for structs) and one for all other variables (including structs). Using typedef before a struct definition will then treat the struct variable as a type, so if you use
struct car_part {/* Code Here */} CarPart;
(where CarPart is optional)
you'd have to use
struct car_part engine;
to declare a car part.
Whereas if you used a typedef with
typedef car_part {/* Code Here */} CarPart;
you can now use
CarPart engine;
instead.
typedef struct tag {/* Code here */} struct_name;
1) Is there any difference between declaring the actual variable before or after the block code? i.e.
typedef struct tag struct_name
{
/* Code here */
};
vs
typedef struct tag
{
/* Code here*/
} struct_name;
2) Are there ever any advantages to not using typedef for a struct definition, even if you won't declare another struct variable of that type?
3) The following code says that there's a syntax error C2061 with the identifier Node, but I don't see anything wrong with it. I tried adding the keyword struct before each element declaration, but that only gave more errors. Any thoughts?
typedef struct Ticket
{
char customer_name[20];
int ticket_number;
} Ticket;
typedef struct Node
{
Ticket ticket_info;
Node *next;
Node *previous;
} Node;
typedef struct Queue
{
Ticket *front;
Ticket *rear;
int queue_count;
} Queue;
edit: fixed first two lines of code to explicitly state where the element declarations should be.
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.]
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;
1) Your first example is invalid syntax. The correct way is this:
typedef struct tag {
/* ... */
} struct_name;
2) Using typedefs for structures makes them seem like atomic data types. It also allows for you to make the types opaque (so other code blocks can't see the inside of the structure). Personally, I find typedef-ing of structures to be a very bad habit (since the struct identifier helps differentiate structures and typedefs of atomic types).
3) You are trying to use the typedef'd version of the node structure inside itself! You need to use the struct Node identifier for the structure when defining it within itself. Like this:
typedef struct Node {
Ticket ticket_info;
struct Node *next;
struct Node *previous;
} Node;
I referred to the tutorial below and later realized it is wrong way of declaring struct using typedef.
typedef struct
{
char name[namesize];
char address[addresssize];
int YearOfBirth;
int MonthOfBirth;
int DayOfBirth;
} PersonalData;
then declare:
PersonalData x;
However, I believe the right way is
typedef struct personaldataStruct
{
char name[namesize];
char address[addresssize];
int YearOfBirth;
int MonthOfBirth;
int DayOfBirth;
} PersonalData;
then declare:
PersonalData x;
Did the author mislead me? or both ways are correct? please confirm.
Here is the tutorial http://www.iu.hio.no/~mark/CTutorial/CTutorial.html
There's nothing formally "incorrect" about either approach.
The former declares a tag-less struct type and a typedef name PersonalData for that struct type. The latter declares a struct type struct personaldataStruct and a synonymous typedef name PersonalData for that struct type. The personaldataStruct part of the declaration is commonly referred as a "struct tag".
As long as you use PersonalData typedef name for referring to that struct type, as in
PersonalData x;
you will not see any difference between the two declarations. In both cases x will be declared identically.
The latter approach provides you with an alternative way of referring to the same struct type - struct personaldataStruct - if for some reason you will wish to do so. E.g. when using the latter declaration, you can also declare your x as
struct personaldataStruct x;
which is exactly equivalent to the PersonalData x; declaration.
Personally, I prefer to use the approach with struct tag, since it provides me with alternative way of referring to the type, which might come handy in some situations (for example, when the struct type has to refer to itself). But in most non-self-referential cases one will be perfectly fine with the former approach.
Both are correct. The only real problem with the first form (without the tag) is that, because the typedef name doesn't become visible until the end of the definition, there's no way to refer to the structure from within its own definition. That's a common requirement; for example, a node in a linked list, tree, or other graph-like data structure commonly needs to point to other objects of the same type.
Since struct tags and typedef names are in different namespaces (not to be confused with C++ namespaces), there's no need for them to be distinct. It's perfectly acceptable to use the same identifier for both:
typedef struct PersonalData {
/* ... */
struct PersonalData *next;
} PersonalData;
Or, as long as you're going to have both a tag and a typedef anyway, you can forward declare the typedef:
typedef struct PersonalData PersonalData;
/* At this point, "struct PersonalData is an incomplete type. */
struct PersonalData {
/* ... */
PersonalData *next;
};
/* And now "struct PersonalData" is a complete type. */
(But a misspelling can leave you with a typedef that still refers to an incomplete type that's never completed, which can trigger errors that can be difficult to track down. Copy-and-paste is your friend.)
And there is another alternative. The type you've defined already has a name: struct PersonalData. All the typedef does is give that same type a different name. It's nice to be able to use a single identifier as a type name, but it's really not necessary. My own preference is to omit the typedef altogether, and just refer to the type as struct PersonalData:
struct PersonalData {
/* ... */
struct PersonalData *next;
};
struct PersonalData *head;
struct PersonalData fred;
Unless PersonalData is meant to be an opaque type (meaning that code that uses it doesn't need to know that it's a struct), there's some advantage in being explicit.
Plenty of people strongly disagree with me on this point, and really like using typedefs for structures, as you'll probably see in comments. There's nothing wrong with using typedefs like this; it's just not necessary. You should be prepared to read code written by others using either style.
I am using MinGW on Windows. I am building linked list and I am confused with this.
#include <stdio.h>
#include <stdlib.h>
typedef struct Data
{
int x;
int y;
struct BlaBla * next; /*compiles with no problem*/
}List;
int main(void)
{
List item;
List * head;
head = NULL;
return 0;
}
I now that struct can't have struct variable(object, instance of that struct), but can have pointer of that struct type. Didn't know that pointer can be pointer of unexisting type. struct BlaBla * next;(not for linked list, it must be struct Data * next but mean general talking)
Yes, you can, because then the compiler, upon encountering the unknown type name for the first time, assumes that there's somehwere a struct type definition with this name. Then it will forward-declare the struct name for you, let you use it as a pointer, but you can't dereference it nor can you do pointer arithmetic on it (since it's an incomplete type).
The compiler will accept code such as your example:
typedef struct Data
{
int x;
int y;
struct BlaBla * next; /*compiles with no problem*/
}List;
This is okay because the size of pointers is known to the compiler, and the compiler is assuming that the struct will be defined before it is dereferenced.
Because the compiler acts this way, it's possible to do this:
typedef struct Data
{
int x;
int y;
struct Data * next; /* points to itself */
} List;
However, if you were to include the struct inline, like this:
typedef struct Data
{
int x;
int y;
struct BlaBla blaStruct; /* Not a pointer. Won't compile. */
}List;
The compiler can't work out how big struct Data is because it doesn't know how big struct BlaBla is. To get this to compile, you need to include the definition of struct BlaBla.
Note that, as soon as you need to access the members of struct BlaBla, you will need to include the header file that defines it.
It depends on what you mean by "unexisting". If you haven't even declared BlaBla, you'll get an error.
If you've declared it but not yet defined it, that will work fine. You're allowed to have pointers to incomplete types.
In fact, that's the normal way of doing opaque pointers in C.
So, you might think that this is invalid because there's no declaration of struct BlaBla in scope:
typedef struct Data {
struct BlaBla *next; // What the ??
} List;
However, it's actually okay since it's both declaring struct BlaBla and defining next at the same time.
Of course, since definition implies declaration, this is also okay:
struct BlaBla { int xyzzy; };
typedef struct Data {
struct BlaBla *next; // What the ??
} List;
In order to declare a variable or field of a given type, pass one as a parameter, or copy one to another of the same type, the compiler has to know how many bytes the variable or field occupies, what alignment requirements it has (if any), and what other pointer types it's compatible with, but that's all the compiler needs to know about it. In all common dialects of C, a pointer to any structure will always be the same size and require the same alignment, regardless of the size of the structure to which it points or what that structure may contain, and pointers to any structure type are only compatible with other pointers to the same structure type.
Consequently, code which doesn't need to do anything with pointers to a structure except allocate space to hold the pointers themselves [as opposed to the structures at which they point], pass them as parameters, or copy them to other pointers, doesn't need to know anything about the structure type to which they point beyond its unique name. Code which needs to allocate space for a structure (as opposed to a pointer to one) or access any of its members must know more about its type, but code which doesn't do those things doesn't need such information.