This is my first time implementing a doubly-linked list. I would like some feedback on my code. Any comment would be appreciated regarding the quality of the code, bad implementation, memory management, memory leaks, etc.
This is the API:
#include "linked_list.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
void ll_create(linked_list_p list) {
list->head = NULL;
list->tail = NULL;
list->size = 0;
}
static bool is_list_empty (linked_list_p list) {
if (list->head == NULL && list->tail == NULL && list->size == 0) {
return true;
}
return false;
}
int ll_destroy(linked_list_p list) {
if (is_list_empty(list) || list == NULL) {
return -1;
}
linked_list_node_p node_ptr = list->tail;
for(int i = list->size-1; i > 0; i--) {
node_ptr = node_ptr->previous;
node_ptr->next->previous = NULL;
node_ptr->next->next = NULL;
free(node_ptr->next);
node_ptr->next = NULL;
}
free(list->head);
list->head = NULL;
list->tail = NULL;
list->size = 0;
return 0;
}
int ll_get(linked_list_p list, uint32_t index, uint8_t *data){
if (is_list_empty(list) || index >= list->size || list == NULL) {
return -1;
}
linked_list_node_p node_ptr = list->head;
for (int i = 0; i < index; i++){
if (node_ptr == NULL) {
return -1;
}
node_ptr = node_ptr->next;
}
*data = node_ptr->data.data;
return 0;
}
int ll_remove_first(linked_list_p list) {
if (is_list_empty(list) || list == NULL) {
return -1;
}
if (list->size == 1) {
free(list->head);
list->head = NULL;
list->tail = NULL;
list->size --;
return 0;
}
linked_list_node_p node_to_remove = list->head;
list->head = list->head->next;
free(node_to_remove);
node_to_remove = NULL;
list->size --;
return 0;
}
int ll_remove_last(linked_list_p list) {
if (is_list_empty(list) || list == NULL) {
return -1;
}
if (list->size == 1) {
return (ll_remove_first(list));
}
linked_list_node_p node_to_remove = list->tail;
list->tail->previous->next = NULL;
list->tail = list->tail->previous;
node_to_remove->previous = NULL;
free(node_to_remove);
node_to_remove = NULL;
list->size --;
return 0;
}
int ll_remove(linked_list_p list, uint32_t index) {
if (is_list_empty(list) || index >= list->size || list == NULL) {
return -1;
}
linked_list_node_p node_to_delete;
if (index == 0) {
return (ll_remove_first(list));
}
if (index == list->size-1) {
return (ll_remove_last(list));
}
linked_list_node_p node_ptr = list->head;
for (int i = 0; i < index-1; i++) {
node_ptr = node_ptr->next;
}
node_to_delete = node_ptr->next;
node_ptr->next->next->previous = node_ptr;
node_ptr->next = node_ptr->next->next;
node_to_delete->next = NULL;
node_to_delete->previous = NULL;
free(node_to_delete);
node_to_delete = NULL;
list->size--;
return 0;
}
int ll_insert(linked_list_p list, uint8_t data, uint32_t index) {
if (list == NULL || index >= list->size) {
return -1;
}
if (is_list_empty(list) || index == 0) {
return (ll_add_first(list, data));
}
linked_list_node_p node_to_add = calloc(1, sizeof(linked_list_node_t));
node_to_add->data.data = data;
linked_list_node_p node_ptr = list->head;
for (int i = 0; i < index-1; i++) {
node_ptr = node_ptr->next;
}
node_to_add->next = node_ptr->next;
node_to_add->previous = node_ptr;
node_ptr->next->previous = node_to_add;
node_ptr->next = node_to_add;
list->size++;
return 0;
}
int ll_add_last(linked_list_p list, uint8_t data) {
if (list == NULL) {
return -1;
}
linked_list_node_p new_node = calloc(1, sizeof(linked_list_t));
if (new_node == NULL) {
return -1;
}
new_node->data.data = data;
if (is_list_empty(list)){
list->head = new_node;
list->tail = new_node;
list->size++;
return 0;
}
new_node->previous = list->tail;
list->tail->next = new_node;
list->tail = new_node;
list->size++;
return 0;
}
int ll_add_first(linked_list_p list, uint8_t data) {
if (list == NULL) {
return -1;
}
linked_list_node_p new_node = calloc(1, sizeof(linked_list_t));
if (new_node == NULL) {
return -1;
}
new_node->data.data = data;
if (is_list_empty(list)) {
list->head = new_node;
list->tail = new_node;
list->size++;
return 0;
}
list->head->previous = new_node;
new_node->next = list->head;
list->head = new_node;
list->size++;
return 0;
}
ssize_t ll_size(linked_list_p list) {
if (is_list_empty(list) || list == NULL) {
return -1;
}
return list->size;
}
int ll_contains(linked_list_p list, uint8_t data) {
if (is_list_empty(list) || list == NULL) {
return -1;
}
linked_list_node_p node_ptr = list->head;
for (int i = 0; i < list->size; i++) {
if (node_ptr == NULL)
return -1;
if (data == node_ptr->data.data) {
return 1;
}
node_ptr = node_ptr->next;
}
return 0;
}
int ll_first_index(linked_list_p list, uint8_t data) {
if (is_list_empty(list) || list == NULL) {
return -1;
}
linked_list_node_p node_ptr = list->head;
for (int i = 0; i < list->size; i++) {
if (node_ptr == NULL)
return -1;
if (data == node_ptr->data.data) {
return i;
}
node_ptr = node_ptr->next;
}
return -1;
}
int ll_indexes(linked_list_p list, uint8_t data, uint32_t **indexes, uint32_t *indexes_size) {
if (is_list_empty(list) || list == NULL) {
return -1;
}
if (indexes_size == NULL) {
indexes_size = calloc(1, sizeof(uint32_t));
}
else {
*indexes_size = 0;
}
linked_list_node_p node_ptr = list->head;
for (int i = 0; i < list->size; i++) {
if (data == node_ptr->data.data) {
(*indexes_size) ++;
*indexes = (uint32_t *)realloc(*indexes, (*indexes_size) * sizeof(uint32_t));
(*indexes)[*indexes_size -1] = i;
}
node_ptr = node_ptr->next;
}
return 0;
}
void ll_print(linked_list_p list) {
linked_list_node_p node_ptr = list->head;
for (int i = 0; i < list->size; i++) {
printf("[%d] --> %d\n", i, node_ptr->data.data);
node_ptr = node_ptr->next;
}
printf("\n");
}
void ll_print_backward(linked_list_p list) {
linked_list_node_p node_ptr = list->tail;
for (int i = list->size-1; i >= 0; i--) {
printf("[%d] --> %d\n", i, node_ptr->data.data);
node_ptr = node_ptr->previous;
}
printf("\n");
}
This is the main used to test the API for errors :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linked_list.h"
int main(int argc, char *argv[]){
static linked_list_t list;
ll_create(&list);
ll_remove(&list, 10);
ll_add_last(&list, 2);
ll_remove_last(&list);
ll_insert(&list, 13, 0);
ll_remove(&list, 32);
ll_add_last(&list, 3);
ll_add_last(&list, 4);
ll_print(&list);
ll_destroy(&list);
ll_print(&list);
ll_add_last(&list, 5);
ll_add_last(&list, 6);
ll_add_first(&list, 7);
ll_print(&list);
ll_print_backward(&list);
ll_insert(&list, 8, 4);
printf("list size : %ld\n", list.size);
ll_insert(&list, 8, list.size);
ll_print(&list);
//TODO use every API functions
// ll_get
uint8_t get_data = 0;
ll_get(&list, 95, &get_data);
printf("got data : %d\n\n", get_data);
// ll_remove
ll_remove(&list, 5);
ll_print(&list);
// ll_insert
ll_remove(&list, 0);
ll_insert(&list, 10, 2);
ll_print(&list);
// ll_contains
uint8_t data_to_find = 96;
printf("The data %d is in the list : %s", data_to_find, ll_contains(&list, data_to_find) ? "true\n" : "false\n");
data_to_find = 10;
printf("The data %d is in the list : %s", data_to_find, ll_contains(&list, data_to_find) ? "true\n" : "false\n");
printf("\n");
// ll_first_index
data_to_find = 10;
printf("The data %d is at node [%d]\n", data_to_find, ll_first_index(&list, data_to_find));
data_to_find = 96;
printf("The data %d is at node [%d]\n", data_to_find, ll_first_index(&list, data_to_find));
// ll_indexes
ll_add_last(&list, 25);
ll_add_first(&list, 25);
ll_insert(&list, 25, list.size);
ll_insert(&list, 25, 2);
ll_print(&list);
uint32_t *indexes = NULL;
uint32_t indexes_size = 0;
if (ll_indexes(&list, 25, &indexes, &indexes_size) == 0) {
for (int i = 0; i < indexes_size; i++) {
printf("%d ", indexes[i]);
}
printf("\n");
free(indexes);
indexes = NULL;
}
if (ll_indexes(&list, 6, &indexes, &indexes_size) == 0) {
for (int i = 0; i < indexes_size; i++) {
printf("%d ", indexes[i]);
}
printf("\n");
free(indexes);
indexes = NULL;
}
if (ll_indexes(&list, 7, &indexes, &indexes_size) == 0) {
for (int i = 0; i < indexes_size; i++) {
printf("%d ", indexes[i]);
}
printf("\n");
indexes = NULL;
}
ll_destroy(&list);
return 0;
}
This is the header structs and typedefs :
/**
* No modification required here
*/
typedef struct data_s{
uint8_t data;
} data_t;
/**
* To be implemented : double linked list
*/
struct linked_list_s;
struct linked_list_node_s;
typedef struct linked_list_s linked_list_t;
typedef linked_list_t *linked_list_p;
typedef struct linked_list_node_s linked_list_node_t;
typedef linked_list_node_t *linked_list_node_p;
struct linked_list_node_s{
linked_list_node_p next;
linked_list_node_p previous;
data_t data;
};
struct linked_list_s{
linked_list_node_p head;
linked_list_node_p tail;
size_t size;
void (*print_data)(uint8_t);
};
ll_remove_last()
andll_print_backward()
were dropped, then a solution could efficiently do other functions with only a single-linked-list. Really want those 2 functions? \$\endgroup\$ll_remove_last()
andll_print_backward()
are the only 2 functions that need.previous
. Without those, the node is 2/3 the size. Yet if you need that functionality, then this savings is not an option. It is not a "problem" as much as a memory savings. \$\endgroup\$