Creating a Python-like list in C
Tags: c, algorithms, lists
Published: 2021-08-11 12:42:40

Instead of arrays in C, Python uses lists. One major upside of lists is that their size is not fixed and that makes appending to/removing from/sorting them a lot easier than arrays. In this article, you'll learn how to create a simple list in C.

**Note: In this implementation of a list in C you can't append elements of different datatypes, here only `char *` elements will be used. However, if you want a list of `int`s or `float`s it will be very easy to do: just replace all `char *`s with `int`s or `float`s.**

In order to make this work, instead of using arrays we should use pointers to arrays, which will allow us to easily change the pointer from one array to another which will make an effect of resizing the original array, although a new one will be used.

### 1. Writing a custom struct for the list
In order to make the whole code cleaner and shorter I want to package all the information about the list into a single variable. To do that I'll create a custom `struct`.

Here is how to do that:

```c
// using struct to package all information into a single variable
typedef struct
{
    char **list;
    int length;
} string_list;
```

### 2. Creating an init function
Creating a list will be a 3-step process:
- initializing the variable
- allocating memory for the list
- setting the initial length of the list

So, if you are planning on using more than one list or if you just want the code design to be better it is a good idea to create a separate function for it.

Here is how that function should look like:

```c
// if you are planning on using several lists, it's better to create an init function
void init(string_list *list)
{
    list->list = calloc(1, sizeof(char *));
    list->length = 0;
}
```
**Note that instead of using `string_list list` I am using a pointer to the list (`string_list *list`), because I want to modify the list itself, not just a copy of it.**

### 3. Creating an append_item function
The whole point of writing code for a custom list in C is that it will be a lot easier to use it because it you don't have to manually resize it. So, here is how to write an `append_item` function:
```c
void append_item(string_list *list, char *string) // note that I am using "string_list *" instead of just "string_list".
{
    // it is okay to use a fixed-sized array here because it is a temporary array
    char *temp_array[list->length + 1]; // + 1 is for a new element

    // adding the list->list elements to the temp_array
    for (int i = 0; i < list->length; i++)
    {
        temp_array[i] = list->list[i];
    }

    // adding the new element to the temp_array
    temp_array[list->length] = string; // 0 is the first number, that's why "list->length" instead of "list->length + 1"

    // freeing the list->list (it should be allocated to make this function work)
    free(list->list);

    // allocating memory for list->list
    list->list = calloc(list->length + 1, sizeof(char *)); // I am using calloc instead of malloc here because it just makes the code cleaner, apart from that there is no difference between them

    // adding elements from temp_array to list->list
    for (int i = 0; i < list->length + 1; i++)
    {
        list->list[i] = temp_array[i];
    }

    list->length++;
}
```

### 4. Creating a remove_item function
Quite often you need to remove some element from the list. So, here is how to write a `remove_item` function: 
```C
void remove_item(string_list *list, char *string) // note that I am using "string_list *" instead of just "string_list".
{
    // it is okay to use a fixed-sized array here because it is a temporary array
    char *temp_array[list->length]; // without "-1" because it is uncertain if the list includes the element
    // a variable for tracking, whether or not the list includes the element; set to "false" by default
    int found = 0;

    // adding all elements from list->list to temp_array, except the one that should be removed
    for (int i = 0; i < list->length; i++)
    {
        // if the element is not the one that should be removed
        if (strcmp(list->list[i], string) != 0)
        {
            temp_array[i] = list->list[i];
        }
        else
        {
            found = 1;
        }
    }

    // if not found there is no point of doing the following
    if (found)
    {
        // freeing the list->list (it should be allocated to make this function work)
        free(list->list);

        // allocating memory for list->list
        list->list = calloc(list->length - 1, sizeof(char *)); // I am using calloc instead of malloc here because it just makes the code cleaner, apart from that there is no difference between them

        // adding elements from temp_array to list->list
        for (int i = 0; i < list->length - 1; i++)
        {
            list->list[i] = temp_array[i];
        }

        list->length--;
    }
}
```

### Full code and usage
So, code for basic list of strings is done. If you had any issues while copying the code to your computer, here is the full code which you can copy paste:

```c
#include <stdio.h> // for printf
#include <stdlib.h> // for calloc, malloc and free
#include <string.h> // for strcmp

// using struct to package all information into a single variable
typedef struct
{
    char **list;
    int length;
} string_list;

// if you are planning on using several lists, it's better to create an init function
void init(string_list *list)
{
    list->list = calloc(1, sizeof(char *));
    list->length = 0;
}

void append_item(string_list *list, char *string) // note that I am using "string_list *" instead of just "string_list".
{
    // it is okay to use a fixed-sized array here because it is a temporary array
    char *temp_array[list->length + 1]; // + 1 is for a new element

    // adding the list->list elements to the temp_array
    for (int i = 0; i < list->length; i++)
    {
        temp_array[i] = list->list[i];
    }

    // adding the new element to the temp_array
    temp_array[list->length] = string; // 0 is the first number, that's why "list->length" instead of "list->length + 1"

    // freeing the list->list (it should be allocated to make this function work)
    free(list->list);

    // allocating memory for list->list
    list->list = calloc(list->length + 1, sizeof(char *)); // I am using calloc instead of malloc here because it just makes the code cleaner, apart from that there is no difference between them

    // adding elements from temp_array to list->list
    for (int i = 0; i < list->length + 1; i++)
    {
        list->list[i] = temp_array[i];
    }

    list->length++;
}

void remove_item(string_list *list, char *string) // note that I am using "string_list *" instead of just "string_list".
{
    // it is okay to use a fixed-sized array here because it is a temporary array
    char *temp_array[list->length]; // without "-1" because it is uncertain if the list includes the element
    // a variable for tracking, whether or not the list includes the element; set to "false" by default
    int found = 0;

    // adding all elements from list->list to temp_array, except the one that should be removed
    for (int i = 0; i < list->length; i++)
    {
        // if the element is not the one that should be removed
        if (strcmp(list->list[i], string) != 0)
        {
            temp_array[i] = list->list[i];
        }
        else
        {
            found = 1;
        }
    }

    // if not found there is no point of doing the following
    if (found)
    {
        // freeing the list->list (it should be allocated to make this function work)
        free(list->list);

        // allocating memory for list->list
        list->list = calloc(list->length - 1, sizeof(char *)); // I am using calloc instead of malloc here because it just makes the code cleaner, apart from that there is no difference between them

        // adding elements from temp_array to list->list
        for (int i = 0; i < list->length - 1; i++)
        {
            list->list[i] = temp_array[i];
        }

        list->length--;
    }
}

int main(void)
{
    string_list list;

     // note that I am passing to the functions the pointer to the list, not the list itself
    init(&list);

    append_item(&list, "hello, world");
    append_item(&list, "hello again");
    append_item(&list, "another string");

    for (int i = 0; i < list.length; i++)
    {
        printf("%s\n", list.list[i]);
    }

    printf("----------------\n");

    remove_item(&list, "another string");

    for (int i = 0; i < list.length; i++)
    {
        printf("%s\n", list.list[i]);
    }

    // don't forget to free the list after usage
    free(list.list);

    return 0;
}

```
Output:
```
hello, world
hello again
another string
----------------
hello, world
hello again
```

Hope that helped!

Thanks for reading and feel free to leave a comment if you have encountered an issue - I'll do my best to help you.