I am trying to understand how pointers in linked lists work. So far i am having lots of trouble trying to figure out where the pointer is pointing to and how a pointer of a type struct works( I know we need to allocate memory from the heap but can't quite understand it, but maybe that's a different question altogether).
Lets take this structure:
typedef struct Node {
int data;
struct Node *link;
} Node;
What I think will happen now is:
Say you have a pointer of type Node in the main function, Node* p and this is allocated memory (using malloc).
Now if we have some data p->data=5; , p points to the beginning of this data (at least this is what i think is happening).
Where exactly does link point to?
So now, i come across this particular piece of code:
typedef struct Node {
int data;
struct Node *link;
} Node;
typedef struct List {
Node* head;
int number_of_nodes;
} List;
So this is complete chaos in my brain! .
Now in the structure List, what is head doing? What is it pointing to? And how would you create a linked list at all with these two lists??
I am really trying my level best to understand how linked lists work, but all the pointers make it too hard to keep track of. You might suggest i start with something simple and i did, and i have already mentioned how much i understand. But the head pointer in the second structure has completely thrown me off track!
It would make my life so much more easier if someone could help me explain it while keeping track of the pointers.
Where exactly does link point to?
link points to another object of the same type:
+------+------+ +------+------+ +------+------+
| data | link |---->| data | link |---->| data | link | ----> ...
+------+------+ +------+------+ +------+------+
Now in the structure List, what is head doing? What is it pointing to?
head points to the first node in a list:
+-----------------+ +------+------+ +------+------+
| head |---->| data | link |---->| data | link |----> ...
+-----------------+ +------+------+ +------+------+
| number_of_nodes |
+-----------------+
I am really trying my level best to understand how linked lists work,
Don't feel bad - linked lists threw me for a loop in my Data Structures class (my first "hard" CS class). It took me a solid week longer than my classmates to grok the concept. Hopefully the pictures help.
Edit
what happens if you have a pointer to the structure List, memory allocated and all? Where does it point to then (according to the diagrams, which did help by the way)
So, let's assume you have the following code:
/**
* Create a new list object. head is initially NULL,
* number_of_nodes initially 0.
*/
List *newList( void )
{
List *l = malloc( sizeof *l );
if ( l )
{
l->head = NULL;
l->number_of_nodes = 0;
}
return l;
}
int main( void )
{
List *l = newList();
...
}
Then your picture looks like this:
+---------+ +--------------------+
| l: addr | ----> | head: NULL |
+---------+ +--------------------+
| number_of_nodes: 0 |
+--------------------+
(addr represents some arbitrary memory address)
Now let's say you add a node to your list:
/**
* Create a new node object, using the input data
* link is initially NULL
*/
Node *newNode( int data )
{
Node *n = malloc( sizeof *n );
if ( n )
{
n->data = data;
n->link = NULL;
}
return n;
}
void insertNode( List *l, int data )
{
Node *n = newNode( data );
if ( n )
{
/**
* If list is initially empty, make this new node the head
* of the list. Otherwise, add the new node to the end of the
* list.
*/
if ( !l->head ) // or n->head == NULL
{
l->head = n;
}
else
{
/**
* cur initially points to the first element in the list.
* While the current element has a non-NULL link, follow
* that link.
*/
for ( Node *cur = l->head; cur->link != NULL; cur = cur->link )
; // empty loop body
cur->link = n;
}
l->number_of_nodes++;
}
}
int main( void )
{
List *l = newList();
insertNode( l, 5 );
...
}
Now your picture looks like this:
+---------+ +--------------------+ +------------+
| l: addr | ----> | head: addr | ---> | data: 5 |
+---------+ +--------------------+ +------------+
| number_of_nodes: 1 | | link: NULL |
+--------------------+ +------------+
You could add another node:
int main( void )
{
List *l = newList();
insertNode( l, 5 );
insertNode( l, 3 );
...
}
then your picture becomes
+---------+ +--------------------+ +------------+ +------------+
| l: addr | ----> | head: addr | ---> | data: 5 | +--> | data: 3 |
+---------+ +--------------------+ +------------+ | +------------+
| number_of_nodes: 2 | | link: addr | --+ | link: NULL |
+--------------------+ +------------+ +------------+
Naturally, you'd want to add some error checking and messages in case a node couldn't be allocated (it happens). And you'd probably want an ordered list, where elements are inserted in order (ascending, descending, whatever). But this should give you a flavor of how to build lists.
You'd also need functions to remove items and free that memory. Here's how I'd free an entire list:
void freeList( List *l )
{
Node *prev, *cur = l->head;
while( cur && cur->link )
{
prev = cur;
cur = cur->link;
free( prev );
}
free( cur );
}
int main( void )
{
List *l = newList();
...
freeList( l );
free( l );
...
}
… a pointer of a type struct…
A pointer cannot be of a type struct. A pointer can point to a structure.
C has objects. Objects include char, int, double, structures, and other things. A structure is a collection of objects grouped together.
In main, if you define p with Node *p;, you then have a pointer p. It has no value because you have not given it a value. When you execute p = malloc(sizeof *p);, you request enough memory for the size of the thing p points to (*p). If malloc returns a non-null pointer, then p points to a Node structure.
Then p->data refers to the data member of that structure. p->data is shorthand for (*p).data, in which *p means “the object p points to” and .data means “the data member in that object.”
After p = malloc(sizeof *p); and p->data = 5;, p->link does not point to anything because you have not assigned it a value. In a linked list, you would use malloc to get memory for another Node, and then you would set the p->link in one Node to point to the new Node. In each Node, its link member points to the next Node in the list. Except, in the last Node, p->link is set to a null pointer to indicate it is the last Node.
In List, you would set head to point to the first Node in a list of Node objects.
Related
I'm trying to delete nodes from a simply linked list on C, when I delete any other node except the first it works fine, but when I try to delete the first node the whole list messes up, I've tried different solutions and I have the same outcome, I don't know what to do anymore
One of my tries was this:
void deleteClient (client **p, int n){
client *t = *p;
if (t){
while (t && t->id != n)
t = t->next;
if (t){
client * ax = t;
t = t->next;
free(ax);
}
}
}
The other one was this
void deleteClient (client **p, int n){
client *t = *p;
if (t)
if (t->id == n){
client * ax = *p;
*p = (*p)->next;
free(ax);
return;
}
else{
while (t->next && t->next->id != n)
t = t->next;
if (t->next){
client * ax = t->next;
t->next = t->next->next;
free(ax);
}
}
}
But in both versions of the code it only deletes fine from the second node onwards, while messing up the whole list if I try to delete the first node.
You can eliminate testing for multiple cases (is node the 1st, if not the 1st, etc..) by simply using a pointer-to-pointer to node to hold the current node and a pointer to the next node, e.g.
/** delete node with value n from list (for loop) */
void deleteClient (client **p, int n)
{
client **ppn = p; /* pointer to pointer to node*/
client *pn = *p; /* pointer to node */
for (; pn; ppn = &pn->next, pn = pn->next) {
if (pn->id == n) {
*ppn = pn->next; /* set address to next */
free (pn);
break;
}
}
}
This approach is detailed in Linus on Understanding Pointers
The first question that comes to me, when dealing with your problem is: If you have defined an interface to your function that receives a pointer to a client by reference, why don't you get profit from that fact and use it to modify the received pointer? (I was astonished about this, because the first thing you do, in the function is to dereference it, and use a normal pointer, and you don't touch the original pointer received anymore) If you pass a pointer to the first node, you'll never have access to the pointer variable, and you'll not be able to change its value, and so, you'll never be able to unlink the first element, and it is because of that, that you need to access the pointer pointing to the first node (in order to be able to change it). Very good at passing the pointer by reference, but bad as you didn't know why.
(pointer to 1st el.)
+-----+ +----------------+ +----------------+
--->| *p >---------->| client | next >------->| client | next >------.
+-----+ +----------------+ +----------------+ |
^ V
| NULL
+--|--+
| p | (reference to the pointer that points to the first element)
+-----+
As you move the pointer reference, you get up to this scenario:
+-----+ +----------------+ +----------------+
--->| *p >---------->| client | nxt >-------->| client | nxt >-------.
+-----+ +----------------+ +----------------+ |
^ V
,-----------------------' NULL
+--|--+
| p | (see how the reference points to the pointer, not to client node)
+-----+
From this scenario, with a reference pointed by &p, we need to make the value pointed by the pointer referenced to the next client node's nxt pointer, and not to the node itself. As here:
,---------------------------.
| |
+-----+ | +----------------+ | +----------------+
--->| *p >----' | client | nxt >-----+-->| client | nxt >-------.
+-----+ +----------------+ +----------------+ |
^ ^ =====
| | ===
+--|--+ +-|--+ =
| p | | q | (q points to the client node, instead)
+-----+ +----+
In this graph, q is a node pointer we use to link to the client node we are going in order to free() after it has been unlinked. So, your first approach can be turned into this:
void deleteClient (client **p, int n)
{
/* first advance the reference to the n-esim pointer,
* (zero meaning the first node) We decrement n after
* checking, and test goes on while *p is also non null
*/
while (*p && (*p)->id != n)
p = &(*p)->next; /* move the reference to a reference
* to the pointer in the next node. */
client *q = *p; /* temporary link to the node to be
* freed */
if (q) { /* if found */
*p = q->next; /* make *p point to the next node. */
free(q); /* free the client node */
}
}
The way to call this routine should be:
client *list;
/* ... */
deleteClient(&list, 3); /* delete node with id == 3 */
The statement p = &(*p)->next; needs some explanation:
*p is the address of the client node that the pointer referenced by p points to.
(*p)->next is the next pointer of the node the pointer referenced by p points to.
&(*p)->next is the address of that pointer.
So we make p to point to the address of the next pointer of the client node pointed to by the referenced pointer *p.
NOTE
The reason your code messes up the whole list when you delete the first node is that you make the pointer (the initial pointer to the first node) to point to the second, but that pointer is local to your function and, as you never modify the pointer passed by reference (you modify the copy you make as soon as you get into the function, it is never modified above it), it continues to point to the (now free()d) node, so this makes the mess (not only you have a pointer pointing to an invalid address, you have leaked the rest of the nodes ---as the next field of the pointed node can have been changed by free() as a result of managing the returned memory chunk---) :)
Finally, you have a complete example here, that you can checkout from github.
I have some questions regarding the definition of a linked list as it was defined in my class.
This is what was used:
typedef struct node_t {
int x;
struct node_t *next;
} *Node;
Now, I understand that this way we created a shorter way to use pointers to the struct node_t. Node will be used as struct node_t*.
Now, say we want to create a linked list. For example:
Node node1 = malloc(sizeof(*node1));
Node node2 = malloc(sizeof(*node2));
Node node3 = malloc(sizeof(*node3));
node1->x = 1;
node1->next = node2;
node2->x = 4;
node2->next = node3;
node3->x = 9;
node3->next = NULL;
This is roughly how I imagine this (The circles represent the structures):
Now I know it's wrong, but I can't understand why. We have a pointer, node1, that points to our structure. Then, we point at node2, which points at another structure and so and so on.
Another things is, I can't understand how is it possible to have the longer arrows in the picture. Shouldn't we only be able to point to a structure from each lower part of the circle, and not to a pointer to a structure? How is this possible?
If anyone here could make things a little clearer it would be hugely appreciated. Thank a lot.
You have three linked nodes, and additional local pointers pointing to them.
The nodes don't know anything about those local pointers though, even if it is often convenient to use their names to refer to the nodes.
Instead, they know the next node in the sequence, respectively the last node knows none.
Put another way, your image is flat-out wrong.
+---+------+
node1 --> | 1 | next |
+---+-|----+
|
v
+---+------+
node2 --> | 4 | next |
+---+-|----+
|
v
+---+------+
node3 --> | 9 | NULL |
+---+------+
Assignment is a transitive operation. So,
node1->next = node2;
would mean that node1->next points to whatever node2 was pointing to. And, in particular, node1->next does not point to node2 itself.
Each of node1, node2, and node3 name a variable that is a pointer.
node1 node2 node3
+---+ +---+ +---+
| * | | * | | * |
+ | + + | + + | +
v v v
+---+---+ +---+---+ +---+---+
| 1 | * --> | 4 | * --> | 9 | * --> NULL
+---+---+ +---+---+ +---+---+
typedef struct node_t {
int x;
struct node_t *next;
} *Node; /* <-- don't typedef pointers */
Simply use Node instead of Node * and then allocate with:
Node *node1 = malloc(sizeof(*node1));
Why? Somebody looking at your code 100 lines below the declaration of your typedef will not inherently know whether Node is a type, or whether it is a pointer-to-type. This type of confusion will only grow as your code grows in size. Review: Is it a good idea to typedef pointers?.
(note: good job using the dereferenced pointer to set the typesize in sizeof)
A Linked List
A linked list is simply a clever data structure that allows you to iterate over a number of independently allocated nodes. Each node contains some data and then a pointer to the next node in the list, or NULL if that node is the final node in the list.
(for a doubly-linked list, you simply add a prev pointer that also points to the node before the current node in the list. You also have circular lists where the last node points back to the first allowing iteration from any node to any other node in the list regardless of which node you begin iterating with. For a doubly-linked circular list, you can iterate the entire list in both directions from any node)
In your case, your list is simply:
node1 +-> node2 +-> node3
+------+ | +------+ | +------+
| data | | | data | | | data |
|------| | |------| | |------|
| next |--+ | next |--+ | next |---> NULL
+------+ +------+ +------+
Where your data is a single integer value and your next pointer simply holds the address of the next node in your list, or NULL if it is the final node in the list. Adding your data, your list would be:
node1 +-> node2 +-> node3
+------+ | +------+ | +------+
| 1 | | | 4 | | | 9 |
|------| | |------| | |------|
| next |--+ | next |--+ | next |---> NULL
+------+ +------+ +------+
When creating a list, the first node is usually referred to as the head of the list and the last node the tail of the list. You must always preserve a pointer to the head of your list as that pointer holds the beginning list-address. For efficient insertions into the list, it is also a good idea to keep a pointer to the tail node so you can simply insert the new node without iterating over the list to find the last node each time, e.g.:
Node *newnode = malloc(sizeof(*newnode)); /* allocate */
newnode->next = NULL; /* initialize next NULL */
tail->next = newnode; /* assign to tail */
tail = newnode; /* set new tail at newnode */
Lists are fundamental to C, there are many used in the Linux kernel itself. Take the time to understand them and how to write them in the differing variants. You'll be glad you did. Lastly, don't forget to write a simple function to free your list when you are done (and free the data as well if it is allocated). A simple free_list function would be:
void free_list (Node *list)
{
while (list) {
Node *victim = list; /* separate pointer to node to free */
list = list->next; /* can you see why you iterate next... */
free (victim); /* before you free the victim node? */
}
}
Let me know if you have further questions.
I am having big problems understanding linked lists and I would be very thankful if someone could explain me the following.
Element_t *pushfront(Element_t *list)
{
if(list==0)
return allocate();
list->prev=allocate();
list->prev->next=list;
list=list->prev;
return list;
}
What here means list->prev->next=list ?
What means this: f->next->prev=f->prev?
I know that this is just a part from program code, but I hope someone can give me general meaning of these as simpliest as can?
The list has nodes that have references to the previous node and to the next node in the list.
The function gets the first node of the list. So this first node does not have a previous node.
In this statement
list->prev=allocate();
a previous node is created because as it follows from the function name it push a new node at the beginning of the list.
In this statement
list->prev->next=list;
expression list->prev yields the address of the new created node. This created node shall point to the current first node of the list. Thus its data member next shall contain the address of the node list.
And this statement
list->prev->next=list;
does this.
It can be imagined simpler if to introduce an intermediate variable.
For example
Element_t *new_node = allocate();
list->prev = new_node; // list->prev=allocate();
new_node->next = list; //list->prev->next=list;
list = new_node; //list=list->prev;
return list;
As for this question
What means this: f->next->prev=f->prev?
then it looks like that the node f is removed from the list. Now its next node (f->next) will not point to f but will point to the preceding node of f.
Again it will be more clear if to introduce intermediate variables.
Element_t *previous_node_of_f = f->prev;
Element_t *next_node_of_f = f->next;
next_node_of_f->prev = previous_node_of_f; // f->next->prev=f->prev
If to add also a statement like this
previous_node_of_f->next = next_node_of_f;
then the node f will be fully removed from the list.
--------------------------------------------------------
| prev ^
| |
---------------------- ---------------------- ----------------------
| previous_node_of_f | | f | | next_node_of_f |
---------------------- ---------------------- ----------------------
| ^
| next |
-------------------------------------------------------
Here is your code with comments, which tell you what is happening.
Element_t *pushfront(Element_t *list)
{
if(list==0) // If the list is emtpy
return allocate(); /* then you simply create a new node, which
represents your list and return it. The size of your list grew from 0 to 1.*/
list->prev=allocate(); /*If the list is not empty, you add a new node by creating it,
and then the prev pointer of the first element in the list (list->prev)
is set to this new element, as you want it to be first.*/
list->prev->next=list; /*Then you need to set the next pointer
to the element you just added. The new element is at list->prev,
so by list->prev->next, you just say, that the next element of the one
you just created is the element that was first before you added the new one*/
list=list->prev; /* Here you just set the new element as the head of the list*/
return list; /*And here you return the new list*/
}
Note, that your list is always passed just as a pointer to the first element, as that's all you need. You can then access all of the elements by next pointer, which is set for each element in the list.
Firstly the linked lists are linked each others with pointers. They are not in order in fact they are distributed on memory.
Let me get on your question-1 list->prev->next=list;
Here you are linking the previous node with the current node. This code means that link the next of previous to the current node. Then the previous node now linked with next pointer which is defined in structure.
Get the question-2 f->next->prev=f->prev;
I dont know where this is defined but here you are doing that a circled linked list. The last node linked to the first node by this code.
Element_t is a probably a typedef similar to the following:
typedef struct Element {
struct Element * next;
struct Element * prev;
void * data;
} Element_t;
Assuming this, your linked list basicly works like this:
(list)
A B C
+----------+ +----------+ +----------+
| next=B |--->| next=C |--->| next=0 |
| prev=0 |<---| prev=A |<---| prev=B |
| data="A" | | data="B" | | data="C" |
+----------+ +----------+ +----------+
So now you want to add a new node N in front of A
(list)
N A B C
+----------+ +----------+ +----------+ +----------+
| next=A |--->| next=B |--->| next=C |--->| next=0 |
| prev=0 |<---| prev=N |<---| prev=A |<---| prev=B |
| data="N" | | data="A" | | data="B" | | data="C" |
+----------+ +----------+ +----------+ +----------+
so, 1. A->prev needs to be set to N and 2. N->next needs to set to A.
list->prev=allocate(); this allocates the new N and already assigns A->prev=N.
Next in line: list->prev->next=list: Read list->prev as the new node that just got alloced. Then it's N->next=A - the second step.
And your new element is linked in the list. Obviously, allocate() needs to initialize next and prev to NULL.
What means this: f->next->prev=f->prev?
Depends on where it's written. Here, it's probably part of a function, that removes a node from the list.
I apologize if this might be viewed as a duplicate, but I cannot seem to find a conclusive answer that satisfies my question.
So I have a struct with a self referential pointer to pointers.
struct Node {
int id;
int edge_count;
struct Node **edges;
}
static struct Node s_graph[MAX_ID+1];
I then have a function that allocates some memory.
int add_edge(int tail, int head)
{
struct Node *ptail, *phead;
ptail = &s_graph[tail];
phead = &s_graph[head];
ptail->edges = realloc(ptail->edges, ++ptail->edge_count * sizeof(struct Node *));
if (ptail->edges) {
*(ptail->edges + ptail->edge_count - 1) = phead;
return 0;
}
return -1;
}
The above seems to work just fine. However, I keep seeing posts about pointer to pointers that lead me to wonder if I need to do something like the following in add_edge:
struct Node *phead = malloc(sizeof(struct Node *));
However, this does not seem logical. There should be enough memory for ptail->edges to store this pointer after the realloc call. I am fairly confident that I did the allocation correctly (albeit, inefficiently), but it is kind of sending me on a mind trip ... So when people declare pointer to pointers (e.g., **ptr) and then allocate memory for both ptr and *ptr, wouldn't that technically make ptr a pointer to pointers to pointers (and maybe clearer to declare as ***ptr)? Or maybe I am wrong and missing something conceptually?
Thank you in advance!
It depends on the situation, there is no general answer. If you have a pointer to pointer, eg Node**, and you want to store new data into it, then you need to have two levels of allocations, otherwise one is enough.
struct Node** nodes = calloc(AMOUNT, sizeof(struct Node*));
Now you have an array of struct Node* elements, so each element is a pointer to a struct Node.
Now how do you fill this array? You could want to insert new nodes inside it. Then you wouold require to allocate them, eg
nodes[0] = calloc(1, sizeof(struct Node)); // <- mind Node, not Node*
But in your situation you just want to set the address to an element of an array of the static variable s_graph, so you don't need to allocate a second level, you directly set the value.
So:
struct Node** nodes = calloc(AMOUNT, sizeof(struct Node*));
nodes -> | 0 | 1 | 2 | 3 |
nodes[0] = calloc(1, sizeof(struct Node))
nodes -> | 0 | 1 | 2 | 3 |
|
|
v
| NODE |
But if you have s_graph you already have them allocated, so it's something like:
static struct Node s_graph[MAX_ID+1];
struct Node** nodes = calloc(AMOUNT, sizeof(struct Node*));
nodes -> | 0 | 1 | 2 | 3 |
s_graph -> | N1 | N2 | N3 |
nodes[0] = &s_graph[0];
nodes -> | 0 | 1 | 2 | 3 |
|
|----|
v
s_graph -> | N1 | N2 | N3 |
I want to view all the items of a linked list.
I've created a three items list, and when i use the below "show_items" function, it just show the first element, and the other items can't be showed becuase a segmentation fault error is given by compiler.
#include <stdio.h>
#include <stdlib.h>
struct list{
int age;
struct list *next;
};
void create_item(int *total_items, struct list *where_is_first_item, struct list *where_is_last_item)
{
struct list *generic_item;
generic_item = malloc(sizeof(struct list));
printf("\nage of item %d: ", (*total_items)+1);
scanf("%d", &generic_item->age);
if(*total_items == 0){
where_is_first_item->next=generic_item;
where_is_last_item->next=generic_item;
printf("\nitem created\n");
}
else{
where_is_last_item->next=generic_item;
printf("\nitem created\n");
}
void show_items(int *total_items, struct list *where_is_first_item, struct list *temp){
temp=where_is_first_item->next;
int i;
for(i=0;i<*total_items;i++){
printf("age of element %d: %d\n", i+1, temp->age);
temp=temp->next;
}
}
int main (void){
int total_items=0;
struct list *where_is_first_item;
where_is_first_item=malloc(sizeof(struct list));
struct list *temp;
temp=malloc(sizeof(struct list));
printf("\n\n\tCREATE A NEW ITEM\n");
create_item(&total_items, where_is_first_item, where_is_last_item);
total_items++;
show_items(&total_items, where_is_first_item, temp);
return 0;
}
Well, I see two major issues in the code that you're showing us:
I don't see three members being created like you claim in the problem statement.
The memory returned by malloc() has unspecified contents, and you're expecting it to have useful contents.
And then for just odd things:
Why do you pass total_items as a pointer if you never modify it.
Why do you pass temp as an argument if you promptly ignore the value you passed in?
When iterating through your list, you should be checking the value of temp to make sure it isn't NULL before trying to access it. At the least, you should use the following:
for ( i = 0; i < *total_items && temp != NULL; i++ )
...
although it's more common to iterate through a list like
while ( temp != NULL ) // or just while ( temp )
{
...
temp = temp->next;
}
Your usage of temp seems confused; does it serve any purpose in main after you've called show_items? If not, you should make it local to show_items and not pass it as an argument:
void show_items(int *total_items, struct list *where_is_first_item)
{
struct list *temp = where_is_first_item->next;
...
}
In the code you've posted, total_items is initialized to 0; as written, this code shouldn't attempt to output anything. It would make life easier for everyone if you would either post the actual code you're having problems with, or if it's too big, reduce it to a representative sample of the problem.
Edit
Duh. Now I see the problem. You're never properly setting the next item in your generic_item struct.
So you have two pointers to keep track of the head and tail of your list, where_is_first_item and where_is_last_item. When you add the first item to the list, you create generic_item and set your head and tail pointers to point to it through their respective next members, giving you something like this (using head and tail instead of where_is_first_item and where_is_last_item for brevity):
head generic_item tail
+--+--+ +--+--+ +--+--+
| | -+------->| | -+-??? | | -+---+
+--+--+ +--+--+ +--+--+ |
^ |
| |
+------------------------+
So far so good. However, when you add a second item, you only update the list tail pointer; you don't create an explicit link from the first item to the second, so you wind up with something like the following:
head generic_item tail
+--+--+ +--+--+ +--+--+ +--+--+
| | -+------->| | -+-??? | | -+-??? | | -+--+
+--+--+ +--+--+ +--+--+ +--+--+ |
^ |
| |
+------------------------+
Hopefully you can see the problem. The next pointer in the first list element is never initialized, so it contains an indeterminate value that doesn't correspond to a valid address.
You need an extra step when you append a new element to the list:
struct list *pre = where_is_last_item->next;
pre->next = generic_item;
where_is_last_item->next = generic_item;
That will give you something like the following:
head pre generic_item tail
+--+--+ +--+--+ +--+--+ +--+--+
| | -+------->| | -+------->| | -+-??? | | -+--+
+--+--+ +--+--+ +--+--+ +--+--+ |
^ |
| |
+------------------------+
As a rule, you should always initialize the next member to NULL, rather than leave it indeterminate. It's easy to test against NULL; it's much harder to determine the validity of a non-NULL pointer value.