Implementing a list C - 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.

Related

Cannot push element to the head of the linked list

I'm trying to implement linked list in C and I've got a problem with wrong output. The first element seems not to be pushed in the head of the list.
I expect the below output in the program:
1
1
Actual output:
0
0
Have you ideas what's wrong with the code? Thanks
Problematic code:
main.c
#include <stdio.h>
#include "linked_list.h"
int main(void) {
struct ll_node *node = ll_new();
ll_push_front(node, 1);
printf("%d\n", ll_size(node)); // should print 1, actual output: 0
printf("%d\n", ll_pop_back(node)); // should print 1, actual output: 0
ll_destroy(node);
return 0;
}
linked_list.h
#ifndef LINKED_LIST_H
#define LINKED_LIST_H
#include <stdbool.h>
struct ll_node {
int data;
struct ll_node *prev;
struct ll_node *next;
};
struct ll_node *ll_new();
void ll_destroy(struct ll_node *node);
void ll_push_back(struct ll_node *node, int data);
void ll_push_front(struct ll_node *node, int data);
int ll_pop_back(struct ll_node *node);
int ll_pop_front(struct ll_node *node);
int ll_delete_at(struct ll_node *node, int index);
bool ll_contains(struct ll_node *node, int data);
int ll_find(struct ll_node *node, int data);
int ll_size(struct ll_node *node);
#endif //LINKED_LIST_H
linked_list.c
#include "linked_list.h"
#include <stdio.h>
#include <stdlib.h>
struct ll_node *ll_new() {
struct ll_node* node = (struct ll_node *) malloc(sizeof(struct ll_node *));
if (!node) {
fprintf(stderr, "Error: cannot allocate struct ll_node *node at ll_new");
exit(1);
}
node->prev = NULL;
node->next = NULL;
return node;
}
void ll_destroy(struct ll_node *node) {
while (node->next != NULL) {
struct ll_node *elem = node;
node = node->next;
free(elem);
}
}
void ll_push_back(struct ll_node *node, int data) {
while (node->next != NULL) {
node = node->next;
}
struct ll_node *current = (struct ll_node *) malloc(sizeof(struct node *));
current->data = data;
current->next = NULL;
current->prev = node;
node->next = current;
}
void ll_push_front(struct ll_node *node, int data) {
struct ll_node *current = (struct ll_node *) malloc(sizeof(struct node *));
current->data = data;
current->next = node;
current->prev = NULL;
node->prev = current;
}
int ll_pop_back(struct ll_node *node) {
struct ll_node *current = node;
while (current->next != NULL) {
current = current->next;
}
int old_data = current->data;
free(current);
return old_data;
}
int ll_pop_front(struct ll_node *node) {
// TODO: implement this
}
int ll_delete_at(struct ll_node *node, int index) {
// TODO: implement this
}
bool ll_contains(struct ll_node *node, int data) {
while (node->next != NULL) {
if (node->data != data) {
return false;
}
node = node->next;
}
return true;
}
int ll_find(struct ll_node *node, int data) {
// TODO: implement this
}
int ll_size(struct ll_node *node) {
int size = 0;
while (node->next != NULL) {
++size;
node = node->next;
}
return size;
}
When you push something on to the front of a list, don't you need to return a pointer to the new list?
Surely this code should be changed to return a pointer to current.
void ll_push_front(struct ll_node *node, int data) {
struct ll_node *current = (struct ll_node *) malloc(sizeof(struct node *));
current->data = data;
current->next = node;
current->prev = NULL;
node->prev = current;
}
Then in the calling function, you should be reading that result and storing it as the new list, I think?
struct ll_node *node = ll_new(); allocates a new node (let's call it root), obviously, but then you push a new node into front (let's call it node(1):
root->next = NULL
root->prev = node(1)
node(1)->next = root
node(1)->prev = NULL
In main() node * still points to root, and indeed, it's next pointer is NULL, so ll_size() returns 0. You probably want to return the new root node and do this in main() or pass in the node as struct ll_node ** so you can update it.
node = ll_push_front(node, 1);
Prefer to use the variable rather than type for allocations, and don't cast the void * pointer:
struct ll_node *current = malloc(sizeof *current);
This also fixes the defect of allocating size of a pointer but you require size of the object.
You have a ll_new() function but but ll_push_front() and ll_push_back() implements the same thing again. You should refactor the latter two to use ll_new().
It's not necessarily wrong just confusing that you use struct ll_node to both represent a node of your linked list, and the entire linked list. The root node, for instance, has uninitialized data.
You will need to make similar changes to ll_pop_back(), and you need to update all the relevant pointers.
What should ll_pop_back() do if the list is empty?
ll_destroy() doesn't free the current node.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
struct ll_node {
int data;
struct ll_node *prev;
struct ll_node *next;
};
struct ll_node *ll_new() {
struct ll_node* node = malloc(sizeof *node);
if (!node) {
fprintf(stderr, "Error: cannot allocate struct ll_node *node at ll_new");
exit(1);
}
return node;
}
void ll_push_front(struct ll_node **root, int data) {
if(!root) {
printf("root must be non-NULL");
exit(1);
}
struct ll_node *current = ll_new();
current->data = data;
current->prev = NULL;
current->next = *root;
if(*root)
(*root)->prev = current;
*root = current;
}
int ll_pop_back(struct ll_node **root) {
if(!root) {
printf("root must be non-NULL");
exit(1);
}
if(!*root) {
printf("*root must be non-NULL");
exit(1);
}
struct ll_node *current = *root;
while (current->next != NULL) {
current = current->next;
}
int data = current->data;
if(current->prev)
current->prev->next = NULL;
else
*root = NULL;
free(current);
return data;
}
int ll_size(struct ll_node *node) {
size_t n = 0;
for(; node; node = node->next, n++);
return n;
}
void ll_destroy(struct ll_node *root) {
while(root) {
struct ll_node *tmp = root->next;
free(root);
root = tmp;
}
}
int main(void) {
struct ll_node *root = NULL;
ll_push_front(&root, 'a');
ll_push_front(&root, 'b');
printf("%d\n", ll_size(root));
printf("%c\n", ll_pop_back(&root));
printf("%d\n", ll_size(root));
printf("%c\n", ll_pop_back(&root));
printf("%d\n", ll_size(root));
ll_destroy(root);
}
and the resulting output:
2
a
1
b
0
sizeof (struct node *) != sizeof (struct node):
struct ll_node *current = (struct ll_node *) malloc(sizeof(struct node *));
only allocates memory for the pointer, which will likely be 8 bytes on a 64-bit machine or 4 bytes on a 32-bit machine.
Change it to:
struct ll_node *current = malloc (sizeof *current);
Note that the cast is redundant and may hide a bug. There's an implicit conversion to and from a void *.
One advice I see most often on codereview.com is to allocate based on the size of what an object points to rather than the object type itself. This eases maintainability.
The function is also discarding the return value of malloc(). malloc() returns NULL to indicate failure. I'd suggest returning a bool type for ll_push_front(), true would indicate success, and false can indicate a memory allocation failure.
Changes to local parameters are never reflected back in the caller's memory:
The first element seems not to be pushed in the head of the list.
Your push function is very close to being correct. Remember that C has pass-by-value semantics and pointers are not exempt to that rule. As is, your push function only changes its local copy of head. This change will not be reflected back in the main() function. We need the push function to be able to change the caller's memory. The traditional method to allow a function to change it's caller memory is to pass a pointer to the caller's memory instead of a copy.
So instead of:
struct node *head = /* some value */;
push_node (head, /* some value */);
We pass a pointer to the head pointer, so the changes made to the head pointer in the push function are reflected back in main().
push_node (&head, /* some value */);
And dereference the pointer in the push function to access the caller's memory.
There are other issues in your code. I'd suggest implementing a singly linked list before moving on to this.
For starters within the function ll_new there is incorrectly allocated memory for a node of the list.
struct ll_node* node = (struct ll_node *) malloc(sizeof(struct ll_node *));
Instead you need to write either
struct ll_node* node = (struct ll_node *) malloc(sizeof(struct ll_node ));
or
struct ll_node* node = (struct ll_node *) malloc(sizeof( *node ));
The function does not make a great sense because it does not initialize the data member data of the created node.
The same problem with allocation memory exists also in the function ll_push_front
struct ll_node *current = (struct ll_node *) malloc(sizeof(struct node *));
Again you need to write either
struct ll_node *current = (struct ll_node *) malloc(sizeof(struct node ));
or
struct ll_node *current = (struct ll_node *) malloc(sizeof( *current ));
Actually everywhere in the code you are using incorrect expressions in calls of malloc.
Also the function does not change the pointer to the head node of the list.
It only sets its data member prev to the newly created node
As a result the function ll_size will always return 0 because the data member next of the node pointed to by the pointer node declared in main is equal to NULL
int ll_size(struct ll_node *node) {
int size = 0;
while (node->next != NULL) {
++size;
node = node->next;
}
return size;
}
The function ll_push_back can invoke undefine behavior if a null pointer is passed to the function due to this while loop within teh function
while (node->next != NULL) {
node = node->next;
}
The function ll_pop_back again does not check whether a null pointer is passed
while (current->next != NULL) {
current = current->next;
}
Also if the list contains only one node then the function does not change the passed pointer. The node will be deleted by the pointer that points to the deleted node will stay unchanged.
Or the function ll_destroy does not free all the allocated memory
void ll_destroy(struct ll_node *node) {
while (node->next != NULL) {
struct ll_node *elem = node;
node = node->next;
free(elem);
}
}
leaving the last node in the list undeleted that results in a memory leak due to the condition in the while loop
while (node->next != NULL) {
Your code contains many errors. You need to rewrite it. Start from this declaration in main
struct ll_node *head = NULL;
and try to write at first the function ll_push_front that adds one node to the empty list.
The list shall not have a dummy node.
Pay attention to that it would be better to declare one more structure like for example
struct linked_list {
size_t size;
struct ll_node *head;
struct ll_node *tail;
};
that will indeed specify the double-lonked list.
In this case there will be much efficient to add new nodes to teh tail of the list or to determine the size of the list.
You could define a list in main like
struct linked_list list = { .size = 0, .head = NULL, .tail = NULL };
And a function that adds a node to the beginning of the list could look like
int ll_push_front( struct linked_list *list, int data );
The function can be defined the following way
int ll_push_front( struct linked_list *list, int data )
{
struct ll_node *new_node = malloc( sizeof( *new_node ) );
int success = new_node != NULL;
if ( success )
{
new_node_>data = data;
new_node->next = list->head;
new_node->prev = NULL;
if ( list->head != NULL )
{
list->head->prev = new_node;
}
else
{
list->tail = new_node;
}
list->head = new_node;
++list->size;
}
return success;
}
And the function is called in main like for example
ll_push_front( &list, 1 );
or
if ( !ll_push_front( &list, 1 ) )
{
puts( "Error: not enough memory!" );
}
In turn the function ll_push_back can look the following way
int ll_push_back( struct linked_list *list, int data )
{
struct ll_node *new_node = malloc( sizeof( *new_node ) );
int success = new_node != NULL;
if ( success )
{
new_node_>data = data;
new_node->next = NULL;
new_node->prev = list->tail;
if ( list->tail != NULL )
{
list->tail->next = new_node;
}
else
{
list->head = new_node;
}
list->tail = new_node;
++list->size;
}
return success;
}
To get the size of the list you can write for example
size_t ll_size( const struct linked_list *list )
{
return list->size;
}

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.

Read access violation when trying to compare int?

I am getting a read access violation and I cant understand why. I though I allocated everything correctly and I shouldn't be reading past the end of anything? Any ideas or help is greatly appreciated.
I am trying to read in a file that has a single integer per line and store those into a binary tree and linked list. The tree actually holds the data and the linked list just holds pointers to the nodes in the tree.
The error happens in the insert() function when data and node->num are being compared.
The newNode() function creates both a tree node and a linked list node, it only returns the tree node because of the double pointer handling the linked list.
// Garrett Manley
// delete these when turning it in
#pragma warning(disable : 4996)
#include <stdio.h>
#include <stdlib.h>
// define structs
struct treeNode {
int num;
struct treeNode *right, *left;
};
struct listNode {
struct treeNode *tNode; // data is a pointer to the tree
struct listNode *next;
};
// creates and returns a new node with given data, also adds node to linked list
struct treeNode *newNode(int data, struct listNode *(*list)) {
// make tree node
struct treeNode *node = malloc(sizeof(struct treeNode));
node->num = data;
node->left = NULL;
node->right = NULL;
// make list node
// insert entry into linked list
struct listNode *newNode;
newNode = malloc(sizeof(struct listNode));
newNode->tNode = node;
newNode->next = *list;
*list = newNode;
return (node);
}
// inserts given node into the tree in sorted order
struct treeNode *insert(struct treeNode *node, int data, struct listNode *(*list)) {
if (node == NULL) { // if the tree is empty return new node
return (newNode(data, list));
} else { // if there is a node use recursion to get to the bottom of the tree and add a node in the right spot
if (data <= node->num) {
node->left = insert(node->left, data, list);
} else {
node->right = insert(node->right, data, list);
}
return (node); // return the (unchanged) node pointer
}
}
// print linked list by looping through list, keep looping until node.next == null
// while looping print like this node.data.data to get the tree nodes data
void printList(struct listNode *(*list)) {
struct listNode *tmp = *list;
while (tmp->next != NULL) {
tmp = tmp->next;
printf("here");
}
}
// skim through the file and find how many entries there are
int SCAN(FILE (*stream)) {
int size = 0;
char *str = malloc(100 * sizeof(char));
while (fgets(str, 100, stream) != NULL) {
size++;
}
return size;
}
// loop through the file and load the entries into the main data array
void LOAD(FILE *stream, int size, struct treeNode *(*tree), struct listNode *(*list)) {
rewind(stream);
int i;
char *tmp = malloc(100 * sizeof(char));
for (i = 0; i < size; i++) {
fgets(tmp, 100, stream);
// recursively call insert to create the list, fix
*tree = insert(*tree, atol(tmp), list);
}
}
// free up everything
void FREE(struct treeNode *BlackBox, int size) {
// free linked list first
// then free the tree nodes, do this recursively
}
int main(int argv, char *argc[]) {
FILE *file = fopen("./hw7.data", "r");
int size = SCAN(file);
struct treeNode *tree = malloc(size * sizeof(struct treeNode));
struct listNode *list = malloc(size * sizeof(struct listNode));
LOAD(file, size, &tree, &list);
// print output
// print linked list
//printList(list);
fclose(file);
return 0;
}
There is no need to allocate memory in main for the tree, nodes are allocated by insert as needed. Allocating uninitialized memory for tree and list causes undefined behavior when the members pointed to by these pointers are dereferenced.
Change the initializations to:
struct treeNode *tree = NULL;
struct listNode *list = NULL;
Also note that the parentheses in return (node); are unnecessary, it is customary to write return node; instead.
Was allocating memory twice, removed malloc() in main() and now program works.

How to init a LinkedList?

I want to init my LinkedList, but failed.
typedef int ElemType;
typedef struct LNode
{
ElemType data;
struct LNode* next;
}LNode, *LinkedList;
void Init(LinkedList L)
{
L = (LNode *)malloc(sizeof(LNode));
L->next = NULL;
}
int main()
{
LinkedList head;
Init(head);
return 0;
}
If I Init my LinkedList like this it works.
LNode node = (LNode *)malloc(sizeof(int));
node->data = 5;
node->next = NULL;
LinkedList head = node;
but this will make my head with a value.
void Init(LNode **L, ElemType data)
{
*L = malloc(sizeof(LNode));
(*L)->data = data;
(*L)->next = NULL;
}
int main()
{
LinkedList head;
Init(&head, 0);
// ...
free(head);
return 0;
}
Initializing your head to not contain a value doesn't really make sense, unless you actually want head = NULL which I assume is not the case.
You've chosen to define a linked list as a pointer to an element - the head element.
It's customary to initialize containers (like lists, queues, stacks etc.) to be empty. An empty list has no head element, so the pointer to the head element would be NULL. In other words, you would either write
LinkedList create_list()
{
return NULL;
}
// ...
LinkedList mylist = create_list();
or
void initialize_list(LinkedList* list)
{
*list = NULL;
}
// ...
LinkedList my_list;
initialize_list(&my_list);
The use of malloc() will be in functions which append or prepend a new node to the list.

Passing typedef structure as call by reference in C

I am trying to create a linked list in C and my code is as below.
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *next;
}node_t;
void insert_into_list(node_t *,int);
void print_list(node_t *);
node_t *create_node(int );
void insert_into_list(node_t *head, int value){
node_t *temp ;
temp = create_node(value);
if(head == NULL){
printf("Inserting node for the first time\n");
head = temp;
}else {
head->next = temp;
}
}
void print_list(node_t *head){
node_t *current = head;
while(current!=NULL){
printf("%d----->",current->data);
current = current->next;
}
printf("NULL");
}
node_t *create_node(int value){
node_t *new_node = malloc(sizeof(node_t));
if(new_node==NULL){
printf("Memory allocation failed for the list creation. :(");
return NULL;
}
new_node->data = value;
new_node->next = NULL;
return new_node;
}
int main(int argc, char *argv[]) {
node_t *head = NULL;
insert_into_list(head,10);
if(head==NULL){
printf("Still head is NULL :(");
}else{
printf("Head is not NULL:)");
}
print_list(head);
return 0;
}
In main, I am calling insert_into_list and even after successful memory allocation, i am not able to get the head value with the newly created node. Still showing the value as NULL.
I have debugged with gdb and found that upto below code, head is not NULL
printf("Inserting node for the first time\n");
head = temp;
I thought I am passing by reference and expected the value to get reflected in the caller function.
Please correct me.
If you want to pass by reference (or rather, the equivalent) in C you must pass a pointer. To pass a pointer by reference you have to pass a pointer to the pointer.
So in e.g. insert_into_list you must declare head as a pointer to a pointer:
void insert_into_list(node_t **head, int value)
And use the dereference operator when accessing the head variable.
You call it using the address-of operator &:
node_t *head = NULL;
insert_into_list(&head,10);

Resources