2

I'm learning how to create header files and separate the implementation while also starting to learn about how to create a linked list. I created linked_list.h, linked_list.c, and main.c files. In main.c I get an error when trying to create the struct I typedef'ed unless I create it as a pointer, then it works. I don't understand why this is, could someone please explain this?

linked_list.h:

#ifndef LINKED_LIST_H_
#define LINKED_LIST_H_

typedef struct ListNode_ ListNode;

#endif // LINKED_LIST_H_

linked_list.c:

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

struct ListNode_ {
    int data;
    ListNode *next;
};

main.c:

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

int main() {

    ListNode node1; // why can't I do this?
    ListNode *node2;    // but this works
}
Austin
  • 6,921
  • 12
  • 73
  • 138
  • 2
    Since `main.c` does not include the definition of `struct ListNode_`, you cannot create objects of that type in that translation unit. – Kerrek SB Jul 04 '16 at 00:29
  • I'm a beginner I've never heard of the term translation unit before. Is there something I should change to make it work properly? Why am I able to create ListNode in main with the pointer, but not without? – Austin Jul 04 '16 at 00:30
  • 2
    Move your `ListNode_` structure definition to `.h` file. See [this answer](http://programmers.stackexchange.com/a/167751) to learn more about what information should be stored in header files. – Jezor Jul 04 '16 at 00:32
  • Thanks that worked, but I thought it was recommended to have a separate `.h` and `.c` file instead of defining it in the header, or am I misunderstanding when or where to do that? – Austin Jul 04 '16 at 00:37
  • 1
    @jake Usually you would put functions that operate in linkedlist in linkedlist.c , for example, `add`, `remove`, etc. In the linkedlist.h you put structure declaration and function prototype declarations. – sps Jul 04 '16 at 00:40
  • 1
    See: [What is an opaque pointer in C](http://stackoverflow.com/questions/7553750/), [Why should we typedef a struct so often in C](http://stackoverflow.com/questions/252780/) and [typedef struct vs struct definitions](https://stackoverflow.com/questions/1675351/) — and probably others too. – Jonathan Leffler Jul 04 '16 at 01:25
  • 1
    A 'translation unit' is simply a source file plus the contents of headers and other files that the source file includes. It's the full set of code that the compiler considers when you ask it to compile a single source file. – Jonathan Leffler Jul 04 '16 at 01:29

3 Answers3

2

You need to think about what the compiler does when you declare a ListNode in main.c. It sees the typedef to struct ListNode_, but it doesn't know what struct ListNode_ is when it is compiling main.c. To fix this, you need to move the definition of struct ListNode_ to linked_list.h.

The declaration of a pointer to ListNode works because it is possible to declare a pointer to an object of an unknown size, such as struct ListNode_ in this case. However, if you wish to declare a full-blown ListNode, the compiler needs to know what size it is. But since the compiler compiles each source file separately, it does not know about the struct ListNode_ in linked_list.c, and thus throws an error.

built1n
  • 1,518
  • 14
  • 25
2

This is because despite how both ListNode and ListNode_ have been declared, only ListNode_ has been defined. The compiler doesn't know what a ListNode object can contain or do.

Yes, you can have pointers to ListNode. However, you cannot use them. Try typing

//ListNode node1; // why can't I do this?
ListNode *node2;    // but this works
node2->foo();

and you'll get the same compilation error message about an incomplete type. It's ok only to have the pointer stored somewhere because it's only an address after all and the program only needs to know what it points to.

And yes, you don't use node1 but it still gives you an error. Why? Because the program will automatically have to allocate space for node1 and it has no idea how much that space should be. Not to mention it's supposed to automatically call its constructor, which, again, isn't defined.

This is also why here

struct ListNode_ {
    int data;
    ListNode *next;
};

you're allowed to have that pointer.

I'm ignoring the fact that the files haven't even been linked properly because that was discussed in the comments and it has nothing to do with this issue anyway.

Andros Rex
  • 372
  • 1
  • 7
2

Since you've used multiple files, you've to be a little more careful to make sure that all files have the same view of your struct. In particular, you should consider defining struct ListNode and typedefing it inside the header file linked_list.h itself, as so:

struct Listnode_ {
    int data;
    struct ListNode_ *next;
};

typedef struct Listnode_ Listnode;

The reason you can't declare the actual variable is because main.c doesn't know what its structure is, so it can't allocate memory for it. You CAN however declare a pointer, because all pointers have the same structure internally, no matter what type they are. This is called the PIMPLE concept (Pointer to IMPLEmentation), where you can declare pointers pointing to some implementation even if you don't know what it is - it's a form of abstraction that C offers.

cs95
  • 379,657
  • 97
  • 704
  • 746