Hi everyone, I'm currently working on a school assignment and came up with this error saying 'list_t has no member named head' but when I actually look at list.h and list.c (my code accessing list is in a separate file), head is in list.c. What did I do wrong?

list.c
typedef struct node node_t;

struct list {
    node_t *head;
};

struct node {
    void *data;
    node_t *next;
};



list.h
typedef struct list list_t;


flange.c
vector_t start;
vector_t end;
list_t *params = datacons_params(shape);
list_t *tempList = tuple_val((value_t *)list_nth(params, 0));
start.x = tempList->head->data;
start.x = tempList->head->next->data;

and tuple_val returns (list_t *val)

Dani AI

Generated

The error comes from compiling flange.c with only an incomplete declaration of struct list visible. In list.h you have a typedef to an unknown struct, so the type is opaque in flange.c. You can form and pass around pointers to an opaque type, but you cannot dereference it or access members because the layout is not known to that translation unit. The definition that contains the head member lives in list.c and is not visible at compile time to flange.c, so the compiler rightly reports that list_t has no member named head. This is the intended effect of the opaque pointer pattern (opaque pointer; C11 6.7.2.1 and 6.2.5 on incomplete types in N1570).

If you are not allowed to change list.h to expose the struct definition, you should treat list_t as an opaque handle and only use the API in list.h to traverse or query it. A minimal, compatible approach is to add accessors instead of reaching into internals:

/* list.h */
typedef struct list list_t;
const void *list_first(const list_t *l);

/* list.c */
const void *list_first(const list_t *l) { return l ? l->head ? l->head->data : NULL : NULL; }

/* flange.c */
const void *p = list_first(params); /* cast p to your expected payload type */

Also note that node->data is a void*, so assigning it directly to a numeric field like start.x requires an appropriate cast or conversion.

Recommended Answers

All 3 Replies

You should struct/class declarations in your header file (.h)

list.h

struct list {
    node_t* head;
};

typedef struct list list_t;

Because when you rename the structure list to list_t but nowhere was struct list defined before that line.

I think I understand what you mean but both list.c and list.h are given and I don't think I'm supposed to change the code in them. Also, the code given in list.c had no problem in accessing head.

some code in list.c
list_t *list_empty(void)
{
  list_t *l;

  l = (list_t *)util_malloc(sizeof(list_t));
  l->head = NULL;

  return l;
}

Because when you rename the structure list to list_t but nowhere was struct list defined before that line.

That's not a problem. You can use an incomplete type as the alias in a typedef, provided there's no instantiation of the typedef that would require the alias to be complete until there's a definition for the alias. Since the only use of list_t prior to definition of struct list is through pointers to list_t, all is well.

My guess would be that list.h is included, but list.c either isn't in the build or is being linked in the wrong order. So flange.c sees the definition of list_t, but not the definition of struct list.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.