#include<stdio.h>
typedef struct data
{
int num;
struct node *link;
}node;
main()
{
node *s=NULL,*e=NULL,*new;
new=malloc(sizeof(node));
s=malloc(sizeof(node));
s->num=10;
s->link=new;
new->num=8;
new->link=NULL;
// printf("%d\n",s->link->num); //// error: dereferencing pointer to incomplete type
e=s;
while(e!=NULL)
{
printf("%d",e->num);
e=e->link;
}
}
For the above C program I got the correct output. But if I include the commented line, it generates a dereferencing incomplete type error. Can anyone explain the reason?
The “dereferencing pointer to incomplete type” error commonly occurs in C when one tries to dereference a type (usually a struct) that is: not declared at all. declared, but not defined.
Dereferencing a pointer to a structure creates an expression whose value is the entire structure. Together, the assignment and indirection operators can copy an object created dynamically on the heap to an automatic or local variable on the stack.
Dereferencing is used to access or manipulate data contained in memory location pointed to by a pointer. *(asterisk) is used with pointer variable when dereferencing the pointer variable, it refers to variable being pointed, so this is called dereferencing of pointers.
An incomplete type is a type that describes an identifier but lacks information needed to determine the size of the identifier. An incomplete type can be: A structure type whose members you have not yet specified. A union type whose members you have not yet specified.
This is a naming confusion.
node
== struct data
thanks to typedef
.
But node
!= struct node
. They are 2 distinct types. And struct node
has not been defined.
This is a pity that such a confusing construction is allowed by the C standard, and not even flagged as a warning by compilers. But that's the way C is defined today, so we have to live with it.
My recommendation : don't use the same names for struct
and typedef
. Create your own convention, for example struct thing_s
and typedef struct thing_s thing_t;
. This will avoid naming confusions such as this one.
The solution is now pretty obvious. Replace :
typedef struct data { int num; struct node *link; } node;
by
typedef struct data { int num; struct data *link; } node;
and the problematic printf
will now work.
But your question is : why does your program worked at all without this printf
?
Now that's an interesting one.
Let's go back to the initial definition.
As we said, struct node
doesn't exist, and is therefore incomplete.
Just to better follow what we are going to explain, let's call it struct incomplete_s
instead. Now, node
becomes :
typedef struct data { int num; struct incomplete_s *link; } node;
It will still work without the problematic printf
.
The reason is, node
is properly defined. It is a structure with known size and types. Because it doesn't matter that struct incomplete_s
is incomplete, since link
is defined as a pointer to it. You could as well have defined void * link;
and it would still work.
void*
or struct incomplete_s *
or whatever *
have all same size : they are all pointers. So the structure hosting them can be properly created.
In your main loop, your program does :
while(e!=NULL)
{
printf("%d\n",e->num);
e=e->link;
}
that is e
, which is a pointer node*
, takes the value of e->link
, which is a pointer struct incomplete_s *
.
Note that both pointers are supposed to point to different types. But they are both pointers, so yeah, this assignment is technically possible and authorized by the standard.
Now a more cautious compiler would likely issue a warning here, since you are not supposed to mix pointers of different types. This is a silent type-casting, which is a recipe for future bugs.
I don't know which compiler you use, but you could increase its warning level (use "warning level 4" for Visual, or -Wall -Wextra
for gcc), and it will likely not like this =
operation.
A more explicit type casting would solve that (*) :
e = (node*)(e->link);
Now this is no more silent, programmer is in charge, warnings will disappear.
e->link
definitely exists, so compiler can grab this value.
But s->link->num
doesn't exist, since s->link
is struct incomplete_s*
, and we don't know what it is, so we don't know if it has a num
member.
(*) Overkill complement : At some higher optimization level, this will still be not good : strict aliasing might get in the way. So dereferencing a pointer type into another pointer type remains a dangerous operation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With