This may be a really simple question, but I could not find an answer any place on the internet. I am working on a linked list implementation in C, and am checking the memory usage and de-allocation with valgrind. I have created the following implementation which works properly. The only place that malloc
is called is in the push_int_list
function, which allows a user to create and insert a struct to any index in the linked list. Yes, I know that it is not a true index like an array, but i am using the same lingo to describe the location of a data point. Furthermore, the push_int_list
function will will iterate from the head or tail of the linked list depending on where the struct is to be inserted.
The implementation shown below works correctly; however, when I check the memory allocation and de-allocation with valgrind, it shows that I execute 6 allocs, and 6 frees. As you can see, I only call the push_int_list
function 5 times, and therefore should only be executing 5 allocs and 5 frees. If I change the number of push statements, the number of allocs and frees in valgrind is always one more than I actually execute. Why does valgrind always show one more malloc than I actually execute.
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
// --------------------------------------------------------------------------------
// Linked list Struct to containt data
typedef struct int_list
{
int data;
struct int_list *next;
struct int_list *previous;
} int_list;
// --------------------------------------------------------------------------------
// Struct to track linked list data
typedef struct
{
size_t active_length;
struct int_list *head;
struct int_list *tail;
} Int;
// --------------------------------------------------------------------------------
// Basic initialization of data tracking struct
void init_int_list(Int *list) {
list->active_length = 0;
list->head = NULL;
list->tail = NULL;
}
// --------------------------------------------------------------------------------
// Function that allows user to push a struct to any indice
int push_int_list(Int *list, int data, size_t index) {
if (index < 0 || index > list->active_length) {
fprintf(stderr, "INdex out of range\n");
return -1;
}
struct int_list *dat = malloc(sizeof(int_list));
if (!dat) {
fprintf(stderr, "malloc failed in file %s at line %d\n", __FILE__, __LINE__);
return -1;
}
if (list->active_length == 0) {
// populate struct
dat->data = data;
dat->previous = NULL;
dat->next = NULL;
// Update linked list tracking struct
list->head = dat;
list->tail = dat;
list->active_length += 1;
}
else if (index == 0 && list->active_length > 0) {
// populate struct
dat->previous = NULL;
dat->next = list->head;
dat->data = data;
// Update data in the struct occupying hte next index
(list->head)->previous = dat;
// Update linked list tracking struct
list->head = dat;
list->active_length += 1;
}
else if (list->active_length > 0 && index == list->active_length) {
// populate struct
dat->data = data;
dat->next = NULL;
dat->previous = list->tail;
// Update struct occupying previous index
(list->tail)->next = dat;
// Update linked list tracking struct
list->tail = dat;
list->active_length += 1;
}
// The next two if statements are mean to reduce the iteration time by half
// if the index to be inserted is between 0 and active_length
else if (index < list->active_length / 2) {
struct int_list *current = list->head;
struct int_list *previous = NULL;
for (size_t i = 0; i < index; i++) {
previous = current;
current = current->next;
}
// populate struct
dat->data = data;
dat->next = current;
dat->previous = previous;
// Update previous and next struct
previous->next = dat;
current->previous = dat;
// Update linked list tracking struct
list->active_length += 1;
}
else {
struct int_list *current = list->tail;
struct int_list *next = NULL;
for (size_t i = list->active_length; i > index; i--) {
next = current;
current = current->previous;
}
// Update struct
dat->data = data;
dat->next = next;
dat->previous = current;
// Update previous and next struct
next->previous = dat;
current->next = dat;
// Update linked list tracking struct
list->active_length += 1;
}
return 1;
}
void free_int_list(Int *list) {
if (list->active_length > 0) {
struct int_list *tmp = NULL;
struct int_list *head = list->head;
while (head->next != NULL) {
tmp = head;
head = tmp->next;
free(tmp);
}
free(head);
}
}
// Begin code
int main() {
Int list;
init_int_list(&list);
push_int_list(&list, 1, 0);
push_int_list(&list, 2, 1);
push_int_list(&list, 3, 2);
push_int_list(&list, 4, 3);
push_int_list(&list, 8, 0);
// Print linked list
struct int_list *current = list.head;
for (size_t i = 0; i < list.active_length; i++) {
printf("%d\n", current->data);
current = current->next;
}
free_int_list(&list);
return 0;
}
valgrind information shown below
==10074== Memcheck, a memory error detector
==10074== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==10074== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==10074== Command: ./test
==10074==
8
1
2
3
4
==10074==
==10074== HEAP SUMMARY:
==10074== in use at exit: 0 bytes in 0 blocks
==10074== total heap usage: 6 allocs, 6 frees, 1,144 bytes allocated
==10074==
==10074== All heap blocks were freed -- no leaks are possible
==10074==
==10074== For lists of detected and suppressed errors, rerun with: -s
==10074== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
I have wondered if it is tracking the instantiation of the Int
struct as a malloc, but this is created in stack memory as a static allocation, so I cant see why this would be tracked as a malloc in valgrind!