I'm a C beginner. I'm studying and implementing some data structures. I'd like to get feedback about my style and decisions I made.
This is a Singly Linked List. I decided to keep things as simple as possible writing just the basic operations for the list.
In main
there is some test code.
All the code is in one file because I'd like to store all these implementations in single files (as snippets) for future reference.
By the way this is the first time I post here, please don't be kind.
/*
* Singly Linked List C implementation.
*
* Linear collection of data. Each element (node) contains data and a link to
* the next node.
* The first element is often called "head" and (a pointer to it) must be
* preserved since without it, it's not possible to traverse the list.
* The last element is linked (points) to nothing (NULL).
*
* ----------- ----------- ----------- ----------- ------
* | 17 | --|---->| 3 | --|---->| 22 | --|---->| 11 | --|---->|NULL|
* ----------- ----------- ----------- ----------- ------
* ^
* HEAD
*
* Search: O(n): search operations are performed in linear time.
* Insert: O(1): since all new nodes will replace the head.
* Delete: O(n): since we are deleting the first occurrence of a specific data.
*/
#include <stdlib.h>
typedef struct node
{
int data;
struct node *next;
} node;
/*
* insert: creates and inserts a new node (storing data) in the list pointed by
* the pointer stored at address head.
* Returns the pointer of the new node or NULL if the process failed.
*/
node *insert(node **head, int data)
{
if (head == NULL) {
return NULL;
}
node *new = malloc(sizeof(*new));
if (new != NULL) {
new->data = data;
new->next = *head;
*head = new;
}
return new;
}
/*
* delete: delete the first occurrence of data (i.e the first node which has
* data in its data field) in the list pointed by the pointer stored at
* address head.
* Returns 1 if a node has been deleted, 0 otherwise.
*/
int delete(node **head, int data)
{
if (head == NULL) {
return 0;
}
node *prev = NULL;
node *curr = *head;
while (curr != NULL) {
if (curr->data == data) {
if (prev == NULL) {
*head = curr->next;
} else {
prev->next = curr->next;
}
free(curr);
return 1;
}
prev = curr;
curr = curr->next;
}
return 0;
}
/*
* destroy: delete all nodes in the list pointed by the pointer stored at
* address head, then sets *head to NULL implying an empty list.
*/
void destroy(node **head)
{
if (head == NULL) {
return;
}
node *curr = *head;
node *next;
while (curr != NULL) {
next = curr->next;
free(curr);
curr = next;
}
*head = NULL;
}
/*
* find: returns 1 if there is at least one occurrence of data in the list
* pointed by head, 0 otherwise.
*/
int find(node *head, int data)
{
node *trav = head;
while (trav != NULL) {
if (trav->data == data) {
return 1;
}
trav = trav->next;
}
return 0;
}
#include <stdio.h>
void print_list(node *head);
int main(void)
{
int n;
node *head = NULL;
for (n = 0; n < 10; n++) {
insert(&head, n);
}
print_list(head);
printf("Deleting all elements...\n");
for (n = 0; n < 10; n++) {
delete(&head, n);
}
print_list(head);
printf("Inserting other elements...\n");
insert(&head, 25);
insert(&head, 49);
insert(&head, 57);
insert(&head, 22);
insert(&head, 90);
insert(&head, 27);
insert(&head, 22);
print_list(head);
for (n = 20; n < 30; n++) {
printf("Looking for %i: ", n);
if (find(head, n)) {
printf("%i is in the list\n", n);
} else {
printf("%i is not in the list\n", n);
}
}
printf("\n");
n = 57;
printf("Deleting first occurrence of %i...\n", n);
delete(&head, n);
print_list(head);
n = 22;
printf("Deleting first occurrence of %i...\n", n);
delete(&head, n);
print_list(head);
printf("Deleting head...\n");
printf("Current head: pointer: %p data: %i\n", head, head->data);
delete(&head, head->data);
printf("Head after deletion: pointer: %p data: %i\n", head, head->data);
print_list(head);
n = 25;
printf("Deleting last node...\n");
delete(&head, n);
print_list(head);
printf("Trying functions with NULL head or not stored data...\n");
printf("insert(NULL, 33) returns %p\n", insert(NULL, 33));
printf("find(NULL, 90) returns %i\n", find(NULL, 90));
printf("delete(NULL, 90) returns %i\n", delete(NULL, 90));
node *null_h = NULL;
printf("\nnode *null_h = NULL;\ndelete(&null_h, 90) returns %i\n\n",
delete(&null_h, 90));
printf("delete(&head, 33) returns %i\n", delete(&head, 33));
printf("destroy(NULL)\n");
destroy(NULL);
print_list(head);
printf("Destroying the list\n");
destroy(&head);
print_list(head);
}
void print_list(node *head)
{
printf("List: ");
if (head == NULL) {
printf("is empty");
} else {
node *trav = head;
while (trav != NULL) {
printf(" %i ", trav->data);
trav = trav->next;
}
}
printf("\n\n");
return;
}
main
? Can you be more specific? \$\endgroup\$insert
anddestroy
simply returning when not handed a list. \$\endgroup\$insert
returnsNULL
(failure), probably I should changedestroy
to return abool
(orint
) though. Anyway, what do you suggest for "not swallow errors"? \$\endgroup\$head
againstNULL
because if that will ever happen would be a programming error indeed, hard to detect, and so, leave the program crash while dereferencing, would be better in order to spot the error. Did I understand well? \$\endgroup\$