0

So I am implementing my own linked list to try and get the hang of compiling a project with seperate header and source files. I created a LinkedList.h for definition and a LinkedList.c for implementation.

I found this post to be very informative on typedef vs struct and this informed me that the compiler is complaining about not knowing the definition in the header file (maybe?).

If I move things around I end up with errors where Node is not defined in struct Node { ... } even with a forward-declared typedef struct Node Node.

Let me know if I need to add anything.

Error first:

 make
cc    -c -o Main.o Main.c
Main.c: In function ‘main’:
Main.c:22:16: error: dereferencing pointer to incomplete type 
‘LinkedList {aka struct LinkedList}’
   runner = list->head;
                ^~
<builtin>: recipe for target 'Main.o' failed
make: *** [Main.o] Error 1

makefile

default: Main

Main: Main.o LinkedList.o
    gcc -o Test Test.c -Wall -Wincompatible-pointer-types

LinkedList.h

typedef struct Node Node;
typedef struct LinkedList LinkedList;

Node* CreateNode(unsigned long);
LinkedList* CreateList();
void addTail(LinkedList*, Node*);

LinkedList.c

#include <stdlib.h>
#include <stdio.h>

#include "LinkedList.h"

typedef struct Node {
  unsigned long value;
  Node *next;
} Node;

typedef struct LinkedList {
  unsigned long length;
  Node *head;
  Node *tail;
} LinkedList;

Node* CreateNode(unsigned long value) {
  struct Node *node;
  node = (Node*)malloc(sizeof(Node));
  node->value = value;
  node->next = NULL;
  return node;
}

LinkedList* CreateList() {
  struct LinkedList *list;
  list = (LinkedList*)malloc(sizeof(LinkedList));
  list->head = NULL;
  list->tail = NULL;
  list->length = 0;
}

void addTail(LinkedList *list, Node *node) {
  if (list->head == NULL)
    list->head = node;
  else
    list->tail->next = node;

  list->tail = node;
  list->length += 1;
  return;
}

Main.c

#include <stdio.h>
#include <stdlib.h>

#include "LinkedList.h"

int main(int argc, char *argv[]) {
  LinkedList *list = CreateList();
  addTail(list, CreateNode(12));
  Node *runner;
  runner = list->head;
  while (runner != NULL)
    printf("%lu\n", runner->value);

  return 0;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Jtaks
  • 281
  • 2
  • 5
  • 13
  • 2
    You need to put the structure declaration in `LinkedList.h`, not just in `LinkedList.c`. Otherwise, `main.c` can't access the members. – Barmar Jun 16 '18 at 00:31
  • 2
    Let's look at LinkedList.h carefully. Nobody can see `head` and `value` there, and your compiler cannot find them too while parsing Main.c. – 273K Jun 16 '18 at 00:32
  • Possible duplicate of [typedef struct in header and dereference pointer to incomplete type](https://stackoverflow.com/questions/28377829/typedef-struct-in-header-and-dereference-pointer-to-incomplete-type) – Veltas Jun 16 '18 at 00:39
  • Not a good idea to modify the question with an answer. If you want to answer your own question, post an answer below. Quesiton rolled back. – chux - Reinstate Monica Jun 16 '18 at 01:12

2 Answers2

4

You have to put the structure definitions in the header file, not the code file, so that main.c can refer to the members.

You don't need to use typedef when defining the structure, that will create a duplicate typedef. Just do a forward definition of the type, and define the structure without typedef.

typedef struct Node Node;
struct Node {
  unsigned long value;
  Node *next;
};

typedef struct LinkedList LinkedList;
struct LinkedList {
  unsigned long length;
  Node *head;
  Node *tail;
};

Node* CreateNode(unsigned long);
LinkedList* CreateList();
void addTail(LinkedList*, Node*);
LinkedList* CreateList();

The "Undefined reference" problem is because you're not compiling correctly in the Makefile. The rule for Main should be:

Main: Main.o LinkedList.o
    gcc -o Main Main.o LinkedList.o

See C error: undefined reference to function, but it IS defined

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Well explained. For the solution, I would favour Arkadiy though, since it prevents exposing the internals of the list implementation. – Michael Beer Jun 16 '18 at 00:52
  • So that gets rid of my dereferencing pointer error but now I get `undefined reference to` CreateList, CreateNode, and addTail. Any idea why that may be? – Jtaks Jun 16 '18 at 00:54
  • you need to put all the function declarations in the header file. – Barmar Jun 16 '18 at 00:55
  • You should only have gotten that error for `CreateList`, since it was missing from the header file. I can't see any reason why you'd get that error for the other functions. – Barmar Jun 16 '18 at 00:57
  • @MichaelBeer If you want to avoid exposing the internals, you also need to add a `getValue()` function so you don't need to use `runner->value`. – Barmar Jun 16 '18 at 00:58
  • I got it for all three but it looks like the error is originating from Main.c. LinkedList.o is compiling fine. – Jtaks Jun 16 '18 at 01:02
  • You're not linking the object files together in your makefile. – Barmar Jun 16 '18 at 01:05
  • Thats it! Thanks @Barmar – Jtaks Jun 16 '18 at 01:07
3

A better option would be to add

Node *nextNode(Node*)

to your header file and use that to iterate over the list.

And after that consider having List *createList() to have a place for your root node pointer. It will be handy when you work on removeNode()

Update: To access the value of a node, try adding unsigned long getValue(Node* node). Consider what form the API can take if you try to hide the implementation details behind the API (e.g. an API that would serve resizable array, double linked list and single linked list equally well).