Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dereferencing pointer to incomplete type error when using typedef struct in C

Tags:

c

#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?

like image 741
Sharon Avatar asked Sep 26 '22 06:09

Sharon


People also ask

What is dereferencing pointer to incomplete type?

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.

How do you dereference a structure pointer?

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.

What is dereferencing a pointer in C?

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.

Is an incomplete type?

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.


1 Answers

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.

like image 57
Cyan Avatar answered Oct 12 '22 10:10

Cyan