Double linked list; newbie attempt - c

I've just started out learning C, and (seemingly) so far most stuff is clicking. However, I'm having some trouble tracking down an issue with an attempt at a double linked list. I keep getting a seg-fault when I attempt to build/run this code. I'm compiling with the Cygwin supplied gcc via NetBeans.
I hate to just dump a block of code and say "help", but I don't know what other details are pertinent at this time, so feel free to ask for details if necessary:
#include <stdio.h>
#include <stdlib.h>
struct node_t{
struct node_t *prev;
struct node_t *next;
};
struct list_t{
struct node_t *head;
struct node_t *tail;
int length;
};
struct node_t *new_node(void);
struct list_t *new_list(void);
int append_list_node(struct list_t *list, struct node_t *node);
int main(void) {
int i = 0, length = 0;
struct node_t *node;
struct list_t *list = new_list();
for(i = 0; i < 10; i++){
length = append_list_node(list, new_node());
printf("%d", length);
}
return 0;
}
struct node_t *new_node(void){
struct node_t *node = malloc(sizeof(struct node_t));
return node;
}
struct list_t *new_list(void){
struct list_t *list = malloc(sizeof(struct list_t));
list->length = 0;
return list;
}
int append_list_node(struct list_t *list, struct node_t *new_node){
if(list->head == NULL){
list->head = new_node; // edited
new_node->prev = NULL;
}else{
list->tail->next = new_node;
new_node->prev = list->tail;
}
return (++list->length);
}
Thanks for the super quick responses everyone, all the answers are correct. As I was briefly looking over the code between F5-ing, I realized I wasn't setting the tail, so I resolved to change the line marked edited as follows:
list->head = list->tail = new_node;
I'll also resolve to use calloc() however, I've read that frequent use of it can cause considerable costs to execution time since it's clearing and allocating. Thoughts?

Use calloc() to allocate memory. The malloc() function does not initialize the memory to zeros (so the pointers will be set to NULL). You are making the assumption that the pointers are NULL by default.

C doesn't do any initialization for you. So when you do:
struct list_t *new_list(void){
struct list_t *list = malloc(sizeof(struct list_t));
list->length = 0;
return list;
}
list->head can be anything.. and probably won't be NULL.

One issue is that you never set list->tail to anything, and then attempt to access list->tail->next if list->head isn't NULL (which, as other have pointed out, isn't guaranteed anyway.)

Related

Printing doubly linked list in C goes into infinite loop

Im trying to learn doubly linked lists. I used the following to print:
typedef struct Node{
int data;
struct Node* prev;
struct Node* next;
}node;
typedef struct List{
node *head;
}list;
node * createNode(int data) {
node * newNode = (node*)malloc(sizeof(node));
newNode->data = data;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
_Bool isEmpty(const list *L)
{
if (L->head == NULL)
return 1;
return 0;
}
_Bool insert(list *L, node *N) {
if(isEmpty(L)) {
L->head = N;
}
else{
L->head->prev = N;
N->next = L->head;
L->head = N;
}
if (L->head==N)
return 1;
return 0;
}
void _print(list *L){
node *temp=L->head;
while(temp!=NULL){
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}
int main(int argc, char *argv[]){
list *L1=(list *)malloc(sizeof(list));
node *N1=createNode(3);
node *N2=createNode(1);
node *N3=createNode(5);
insert(L1, N3);
insert(L1, N2);
insert(L1, N1);
_print(L1);
}
for reference my list struct only contains a pointer "head" and my node struct contains next, prev and data.
It prints the correct data but goes into infinite loop.
What is the reason ?
The problem is that this line in main:
list *L1=(list *)malloc(sizeof(list));
Allocates memory for the list, but does not initialize it.
Without initialization the value of L1->head can be anything.
And if it happens to be different than 0 (i.e. NULL), insert will interpret it as pointing to a valid node (which it isn't).
The result is undefined behavior (UB), which means anything can happen. It might seem to work, it can crash, or get into an infinite loop etc.
In order to fix it, you need to initialize the list pointed by L1.
You can do it at least in 2 ways:
Replace the call to malloc with calloc, which also zeroes the allocated memory:
list* L1 = (list*)calloc(sizeof(list), 1);
Add an explicit initialization after the malloc:
list* L1 = (list*)malloc(sizeof(list));
L1->head = NULL; /* <--- initialization */
You can also add a function for encapsulating the initialization.

Double pointer in linked list

I almost figured out this code, but there are two details I can't figure out.
I found this code on YouTube.
source: https://www.youtube.com/watch?v=VOpjAHCee7c
#include <stdio.h>
#include <stdlib.h>
typedef struct node{
int vaule;
struct node *next;
}node_t;
void printlist(node_t *head)
{
node_t *temp = head;
while(temp != NULL)
{
printf("%d - ", temp->vaule);
temp = temp->next;
}
printf("\n");
}
node_t *create_node(int var)
{
node_t *res = malloc(sizeof(node_t));
res->vaule = var;
res->next = NULL;
return res;
}
node_t *insert_at_head(node_t **head, node_t *node_to_insert)
{
node_to_insert->next = *head;
*head = node_to_insert;
return node_to_insert;
}
void find_node()
int main()
{
node_t *tmp;
node_t *head = NULL;
for(int i = 0; i < 15; i++)
{
tmp = create_node(i);
head = insert_at_head(&head, tmp);
}
printlist(head);
return 0;
}
1) Why do we use the nested struct?
typedef struct node{
int vaule;
struct node *next;
}node_t;
I know about nested structures but I didn't understand why we use it here.
2) Why do we use double pointer?
node_t *insert_at_head(node_t **head, node_t *node_to_insert)
{
node_to_insert->next = *head;
*head = node_to_insert;
return node_to_insert;
}
if I change this code like this:
node_t *insert_at_head(node_t *head, node_t *node_to_insert)
{
node_to_insert->next = head;
return node_to_insert;
}
then nothing will change
Why do we use the nested struct?It's not a nested struct. struct node *next is a pointer, and as its name indidcates, it points to the next element.
Why do we use double pointer? Read this: How do I modify a pointer that has been passed into a function in C?
1)Why do we use the nested struct?
It is not a nested struct, but a linked list. Each node has a pointer to the next node (or to NULL for the last node of a list
2)Why do we use double pointer?
C only passes parameters by value. The idiomatic ways to change a variable from the caller are:
assign the return value to that variable. It is the best way, but you can only return one single value that way
pass a pointer to the variable and use the pointer to change the value. As we want to change the value of head which is already a pointer, we have to pass a pointer to pointer.
Here the write of the code has decided to change the passed header to clearly show that it is an input/output parameter, and also returns it because it had no better value to return.

Segmentation Fault 11: 10

I have a problem with solving a problem. I get continue the segmentation fault: 11 error, while I try this code. And every time I change the code the error pops up, and I don't know where the flaw is, so I would be greatfull if anyone sees the flaw.
I thank you in advance.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "dbg.h"
typedef struct node{
char *data;
struct node *next;
} node_t;
node_t **push(node_t **head, char *data){
node_t *new_node;
new_node = malloc(sizeof(node_t));
new_node->data = data;
new_node->next = *head;
*head = new_node;
free(new_node);
return head;
}
int main(int argc, char *argv[])
{
node_t **head;
char *data = "hoi";
char *data2 = "hallo";
head = malloc(20 * sizeof(node_t));
head = push(head, data);
head = push(head, data2);
printf("%s\n",(*head)[1].data);
free(head);
return 0;
}
Flaws:
Your push() function assigns the value of new_node to *head, making it accessible to the invoker of push(), but at the end of the function you free new_node, making it a dangling pointer. That's a good ground for segmentation faults.
head is a pointer to a pointer but is assigned the result of a malloc() invokation that seems to indicate that it should be a pointer to a node.
Your design is confusing: do you want to allocate the memory in push() or in main(). Certainly, both is not a good choice.
You are pointing to constant strings with non constant pointers. This is dangerous. Writing to the constant strings through these pointers could lead to segmentation faults too.
Here is a version of your program that works:
#include <stdio.h>
#include <stdlib.h>
struct node {
const char *data;
struct node *next;
};
static struct node *push(struct node *head, const char *data) {
struct node *node;
node = malloc(sizeof *node);
node->data = data;
node->next = head;
return node;
}
int main(int argc, char *argv[])
{
struct node *head = NULL;
const char *data = "hoi";
const char *data2 = "hallo";
head = push(head, data);
head = push(head, data2);
struct node *node = head;
while (node) {
printf("%s\n", node->data);
node = node->next;
}
return 0;
}
Note that I implemented a LIFO structure, aka. a stack, because a push() function usually applies to a stack.
A logical next step for you would be to implement the pop() function. Typically, I would recommend that pop() frees the node and returns the data. That would provide a nice symmetry for your API.

Incompatible pointer type error using structs

I am new to programming. I am trying to learn C and pointers, but it is giving me much trouble.
I got the following error trying to implement a singly linked list. I searched online, and I couldn't find someone who had an error just like mine, or perhaps I just didn't couldn't make sense of it with my problem.
The following is the error I received:
warning: incompatible pointer types initializing 'NODE *'
(aka 'struct node *') with an expression of type 'struct NODE '
[-Wincompatible-pointer-types]
NODE temp = (*l)->head;
NODE* temp = (*l)->head;
In main, I passed the address of the variable of type LIST. So, I thought I had to dereference 'l', to get the address of where the LIST type is located, then I had to dereference with an arrow to get the address of where the NODE is located. Where am I confused? I do appreciate the help.
Below you will see the code I have written:
typedef struct node {
int value;
struct node* next;
}NODE;
typedef struct list{
struct NODE* head;
}LIST;
void insert(LIST** l, int x){
if((*l)->head == NULL){
NODE* new_Node = (NODE*) malloc(sizeof(NODE));
new_Node->next = NULL;
new_Node->value = x;
}
NODE* temp = (*l)->head;
while(temp->next != NULL){
temp=temp->next;
}
NODE* new_Node = (NODE*) malloc (sizeof(NODE));
temp->next = new_Node;
new_Node->next = NULL;
new_Node->value = x;
}
int main(){
LIST *l = (LIST*) malloc(sizeof(LIST));
insert(&l, 5);
return 0;
}
I guess your problem is here :
typedef struct list
{
struct NODE* head;
}LIST;
just remove struct keyword before NODE
typedef struct list
{
NODE* head;
}LIST;
or
typedef struct list
{
struct node* head;
}LIST;
Also you need to initialize the head with NULL to make this condition to wwork
if((*l)->head == NULL) .....
so when you create your list add l->head = NULL;
LIST *l = malloc(sizeof(LIST));
l->head = NULL;
And the last one (i hope) when you create your first node, you forget to assign head to it, and return in order not to add the first element twice
if((*l)->head == NULL)
{
NODE* new_Node = malloc(sizeof(NODE));
new_Node->next = NULL;
new_Node->value = x;
(*l)->head = new_Node;
return;
}
And BTW, don't cast malloc results in C
This:
typedef struct list{
struct NODE* head;
}LIST;
Should be this:
typedef struct list{
NODE* head;
}LIST;
Tested and compiles fine with that change.
Your use of *l is correct. The problem is with the line:
NODE* temp = (*l)->head;
The left-hand side is NODE *, which is the same as struct node *, however the right-hand side is struct NODE *.
C is case-sensitive, struct node and struct NODE are different types. Also, the namespace of struct tags is separate to that of other types, so NODE and struct NODE are also different types.
I think you meant, in LIST's definition, that struct NODE* head; should be NODE* head;. There is no warning generated on that line, because in C it's legal to implicitly declare a struct type just by mentioning it (i.e. this line declares the new type struct NODE also).
There is code repetition in your insert function. You should not use the struct keyword before NODE in the second typedef statement because NODE is already an alias for the type struct node. That's why you are getting the warning mentioned in your question. Also, you should not cast the result of malloc. Please read this - Do I cast the result of malloc?
I suggest the following changes to your code.
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int value;
struct node *next;
} NODE;
typedef struct list {
NODE *head;
} LIST;
void insert(LIST **l, int x) {
// do not cast the result of malloc.
// also, do not repeat the type on the rhs.
// create the new node to be inserted
NODE *new_Node = malloc(sizeof(*new_Node));
new_Node->next = NULL;
new_Node->value = x;
NODE *temp = (*l)->head;
// check if the head of the list is empty
// if yes, simply assign the new node to head
// and return
if(temp == NULL) {
(*l)->head = new_Node;
return;
}
// reach the last node in the list
while(temp->next != NULL)
temp = temp->next;
// insert the new node to the end of the list
temp->next = new_Node;
}
int main(void) {
LIST *l = malloc(sizeof(*l));
insert(&l, 5);
insert(&l, 10);
// print the value of the head node
printf("%d\n", l->head->value);
// print the value of the next node
printf("%d\n", l->head->next->value);
NODE *head = l->head;
NODE *temp = NULL;
// free the nodes in the list
while(head != NULL) {
temp = head;
head = head->next;
free(temp);
}
// free the pointer to the
// head of the list
free(l);
return 0;
}

Implementing a list C

I have the code with the structure of a list and the code that implements it.
Structure,entry_t is the type of data on the list:
#ifndef _list_private_h
#define _list_private_h
typedef struct list_t{
struct node_t *head;
int size;
};
typedef struct node_t{
struct entry_t *element;
struct node_t *next;
}node_t;
#endif
Code:
struct list_t *list_create(){
struct list_t *list = (struct list_t*) malloc(sizeof(struct list_t));
list->head=NULL;
list->size=0;
return list;
}
int list_destroy(struct list_t *list){
node_t *no = list->head;
while(no!=NULL){
node_t *aux=no;
entry_destroy(no->element);
no=no->next;
free(aux);
list->size=(list->size)-1;
}
free(list);
return 0;
}
int list_add(struct list_t *list, struct entry_t *entry){
node_t *no = list->head;
if(no==NULL){
list->head=(node_t*) malloc(sizeof(node_t));
list->head->element=entry_dup(entry);
list->size=list->size+1;
return 0;
}
else{
while(no!=NULL){
no=no->next;
}
no=(node_t*) malloc(sizeof(node_t));
no->element=entry_dup(entry);
list->size=list->size+1;
return 0;
}
return -1;
}
struct entry_t *list_get(struct list_t *list, char *key){
node_t *no = list->head;
while(no!=NULL){
if(strcmp(no->element->key,key)==0){
return no->element;
}
no=no->next;
}
return NULL;
}
When I run these tests it doesn't add the element to the list:
int testEmptyList() {
struct list_t *list = list_create();
int result = list != NULL && list_size(list) == 0;
list_destroy(list);
printf("Test empty list: %s\n",result?"pass":"not pass");
return result;
}
int testAddHead() {
int result;
struct list_t *list = list_create();
struct entry_t *entry = entry_create(strdup("abc"),data_create(5));
memcpy(entry->value->data,"abc1",5);
list_add(list,entry);
result = list_get(list,"abc") == entry &&
list_size(list) == 1;
list_destroy(list);
printf("Module list -> test add first: %s\n",result?"pass":"not pass");
return result;
}
So, what I want is put this code adding elements to the list. Thanks.
Several issues:
You are destroying the list via list_destroy which can call entry_destroy on the entry added to the list before calling list_get which returns a pointer to (not a copy of) an entry.
In list_add you call malloc to allocate space for a new node, however you don't set its next element to NULL. Since malloc does not guarantee that the memory allocated is wiped, the list may never end with a node that has its next element set to NULL resulting in spurious results.
Your else branch in list_add guarantees no will be NULL (or the program will have crashed from a segfault given earlier problems.) You probably want to terminate when no->next is NULL instead of when no is NULL. Also, this branch needs to assign the next element to NULL explicitly.
Try this:
int list_add(struct list_t *list, struct entry_t *entry){
node_t *no = list->head;
if(no==NULL){
list->head=(node_t*) malloc(sizeof(node_t));
list->head->element=entry_dup(entry);
list->size=list->size+1;
return 0;
}
else{
while(no->next!=NULL){
no=no->next;
}
no->next=(node_t*) malloc(sizeof(node_t));
no->next->element=entry_dup(entry);
no->next->next = NULL;
list->size=list->size+1;
return 0;
}
return -1;
}
The problem is that the previous node needs to know the address of the next one, via the pointer next. In your case, no->next will be equal to NULL (after the loop), so it's the last node. You never assign the next pointer of the last node to the new node, so it will be lost.

Resources