Managing Linked List Memory - c

Recently I have been using C a lot (I normally use higher level languages) and have been running into a problem when managing lists.
Specifically, adding nodes to a linked list. I often end up writing something similar to the sample code below. As far as I can see, it should all work fine however no new nodes are appended to the list.
#include <stdio.h>
#include <stdlib.h>
typedef struct ListNode {
struct ListNode* next;
int data;
} ListNode;
ListNode* createListNode(int data){
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
node->data = data;
node->next = NULL;
return node;
}
void addListNode(ListNode* list, ListNode* newListNode){
ListNode* current;
if(list == NULL){
list = newListNode;
}else{
current = list;
while (current->next != NULL) {
current = current->next;
}
current->next = newListNode;
}
}
int main(void) {
int ii;
ListNode* list = NULL;
ListNode* newListNode = NULL;
int fakeData[3] = {1, 2, 3};
for(ii=0; ii<=2; ii++){
newListNode = createListNode(fakeData[ii]);
addListNode(list, newListNode);
}
printf("%d", list->data);
printf("%d", list->next->data);
printf("%d", list->next->next->data);
return 0;
}
When stepping through the code with a debugger, addListNode works as expected in a local context however, when returning to main the changes are lost and list is still equal to NULL. But list is defined in main so it should be in scope and available until that function ends. Right?
If I make a small change to main like this.
for(ii=0; ii<=2; ii++){
if(ii==0){
list = createListNode(fakeData[ii]);
}else{
newListNode = createListNode(fakeData[ii]);
addListNode(list, newListNode);
}
}
It works fine. Why would that be?
I could do it like this, but I don't want to have linked list specific code floating everywhere.
Clearly, I'm missing something quite fundamental.

void addListNode(ListNode* list, ListNode* newListNode)
This should be ListNode **list, otherwise the function really won't change it's value.
The list = newListNode; command won't have any effect outside the function.
Use ListNode **list, and when you want to change the value just say *list = newListNode;

Related

Any idea why I’m losing links?

I have started to learn about linked lists, and I have written this code.
It should be a recursive call to create a new link in a linked list in c.
But, if you’ll check the output, you’ll see it’s passing over the middle links.
I don’t know why I’m losing the middle links.
Btw, I do have a destroy function in my code, I just didn’t write it here.
I do have a different version of a working code, I don’t ask for solutions, I’m only asking why this recursive idea doesn’t work.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef struct node {
int data;
struct node *next;
}node;
node *create(node **head, int data)
{
if(!*head) {
*head = malloc(sizeof(node));
assert(*head);
(*head)->data = data;
(*head)->next = NULL;
return *head;
}
node *new = NULL;
new = create(&new,data);
(*head)->next = new;
return *head;
}
void display(node *head)
{
assert(head);
node *current = head;
do
{
printf("%d\t",current->data);
current = current->next;
}while(current);
}
int main()
{
int count = 0, data = 0;
node *head = NULL;
printf("Enter list count:\n");
while(count <= 0){
scanf("%d",&count);
if(count <= 0) printf("\nEnter a valid number:\n");
}
while(count){
scanf("%d",&data);
head = create(&head,data);
count--;
}
printf("\nHere are the elements:\n");
display(head);
return 0;
}
As implemented create() either adds a new node to the tail or iterates to the next linked node. Logic changed to affect that. It's confusing that the first argument is called head to changed it to n. Changed main() to retain the head and made the program non-interactive for ease of testing. Recatored display to use a for() loop:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *next;
} node;
node *create(node **n, int data) {
if(!*n) {
*n = malloc(sizeof(**n));
assert(*n);
(*n)->data = data;
(*n)->next = NULL;
return *n;
}
node *n2 = (*n)->next;
(*n)->next = create(&n2, data);
return n2;
}
void display(node *head) {
assert(head);
for(node *c = head; c; c = c->next) {
printf("%d\t", c->data);
}
}
int main() {
node *head = NULL;
node *tail = NULL;
for(int i = 0; i < 10; i++) {
tail = create(&tail, i);
if(!head) head = tail;
}
display(head);
return 0;
}
and it displays:
0 1 2 3 4 5 6 7 8 9
If you compile your code with NDEBUG (some folks do that for production) then your code no longer has any error handling.
Thank you all for your answers. I see the problem now, after “explaining to the duck” a thousand times. In function create(), under the if() block, I assigned (*head)->next = new; without first making it point to the last link, so it’s just over write the next link in every call to the function.
The solution is:
Add a “current” pointer points to the head(to not lose it’s value)
Iterate through the list until we find the last link,
assign current->next the value of new.
Here is the fixed section:
node *new = NULL;
new = create(&new,data);
node *current = *head;
while(current->next) current = current->next;
current->next = new;
return *head;

Problem deleting head node in a doubly linked list in C

I'm trying to implement a doubly linked list in C. While coding it up, I ran into an issue when trying to delete the first element of the list.
Here is is a toy example that illustrates the problem:
#include<stdio.h>
#include <stdlib.h>
typedef struct Node{
struct Node * next;
struct Node * previous;
int data;
}Node;
Node* create_dll(int array[], int arrSize){
Node *current = (Node*)malloc(sizeof(Node));
current->next = NULL;
current->data = array[0];
for(int i = 1; i < arrSize; i++){
Node *temp = (Node*)malloc(sizeof(Node));
temp->data = array[i];
temp->next = current;
current->previous = temp;
current = temp;
}
current->previous = NULL;
return current;
}
void print_dll(Node *head){
if(head != NULL){
Node *current = head;
while(current!=NULL){
printf("%d \t", current ->data);
current = current->next;
}
}
puts(" ");
}
void delete_head(Node *head){
Node *current = head;
head = head->next;
//head ->previous = NULL;
free(current);
}
void kill(Node *head){
Node *current = head;
while (current != NULL){
Node *previous = current;
current = current ->next;
free(previous);
}
}
int main(){
int array [] = {1, 2, 3, 4, 5};
int arrSize = 5;
Node *head;
head = create_dll(array, 5);
print_dll(head);
delete_head(head);
print_dll(head);
kill(head);
return 0;
}
Whenever I try to run the code in main, which creates a DLL, then prints what's in it, then attempts to delete the first node, then print the list again, I get the following result:
5 4 3 2 1
5
Now, I know that one fix would be to make head a global variable, but that will be problematic in other sections of the code, plus I don't really want to go that route. I also don't want to modify any of the function headers, or anything in the main.
I did get this to work by implementing the DLL with a dummy node that head always points to, but I"m sure there is a simple fix to this implementation that avoids all this.
Basically, if I can change what head points to in the delete_head function
and have this change be reflected in the main function, that would be a solution. Otherwise, I would be happy just to understand why this code fails to do what I want.
Any help is very much appreciated! Thanks!
The problem is that when you call delete_head, C parameter passing is by value, so head isn't changed on return. You need to implement it like this:
void delete_head(Node **head){
Node *current = *head;
*head = current->next;
//head ->previous = NULL;
free(current);
}
And call it like this: delete_head(&head);
The trick is, all the external pointers are pointing to individual nodes. So when you cut the head off from the rest of the list, all the pointers to head keep pointing to it—you get the single node on its own, not the rest of the list.
I would solve this by adding an additional struct.
typedef struct DLL{
struct Node * head;
} DLL;
When you want to create the list, create a DLL pointing to the head, instead of returning the head itself. Now when you want to change the head, change the pointer inside the DLL struct. All the references to the DLL itself can stay the same, but now the head inside it has changed, and all those references will see the new head when they look for it!

Inject function in doubly linked list arbitrarily points back to head element after call to pop()

EDIT: I think my question is completely different from the proposed duplicate. It's asking about the general case whereas my question is asking for a very specific case where the reason for the weird behavior should be traceable given how specific it is.
I have some really weird behavior in my doubly LL implementation.
Basically, if I pop() (from the head) an element and then inject() (add at the tail) some other element, that last tail element now points to the head of the list for seemingly no reason (I guess instead of NULL by default or at least a random address).
I figured out how to fix the problem. When injecting, I wasn't pointing the new node's "next" to NULL.
However, I would still like to understand why the injected node would choose to point to the head without specific direction of where to point.
The effect is that if I travel the list starting from the head (but not starting from the tail), I keep looping forever as the last tail element points back to the head of the list.
EDIT: So I tried printing out the address that the pointer is pointing to just after the call to malloc in inject(), and for some crazy reason the pointer is created already pointing to the head's address; but this only happens if I call pop() before calling inject(). Incredibly weird...
int pop()
{
node* temp = head;
int value = temp->value;
head = temp->next;
free(temp);
head->previous = NULL;
size--;
return value;
}
void inject(int value)
{
if (tail == NULL)
{
tail = malloc(sizeof(node));
tail->value = value;
tail->next = NULL;
tail->previous = NULL;
head = tail;
size++;
}
else
{
node* new_node = malloc(sizeof(node));
printf("pointing to: %p\n", new_node->next);// points to head after pop() call
new_node->value = value;
tail->next = new_node;
new_node->previous = tail;
tail = new_node;
//new_node->next = NULL;
size++;
}
}
The commented out line in inject() solves the problem but still doesn't explain why the tail would point back to the head if I inject after a pop.
Below is the code before main() in case:
typedef struct node{
int value;
struct node* next;
struct node* previous;
}node;
node* head = NULL;
node* tail = NULL;
int head_value();
int tail_value();
void push(int);
int pop();
void inject(int);
int eject();
int size = 0;
node* new_node = malloc(sizeof(node));
printf("pointing to: %p\n", new_node->next);// points to head after pop() call
new_node->next will contain whatever garbage malloc wants to put in there. It might happen to point to head, but you never initialized it, so that printf is trying to find meaning in garbage.
Your code scatters memory management all over the place. Rather than try to fix it, let's rewrite it using my stock advice about structs: always write functions to initialize and destroy them. Always, even if it seems silly and trivial. It avoids scattering that code all over the place, doing it slightly differently every time. It allows you to unit test the basic functions of the struct before trying to use it.It lets you focus on the algorithm, not the memory management.
First, let's make a tweak to your struct. node is a very bad name for a type. It's likely you (or somebody else) is going to want to call a variable node and cause a conflict. I've called it Node, capitalized to avoid confusion with variables and builtins.
typedef struct Node {
int value;
struct Node* next;
struct Node* previous;
} Node;
Now we can write Node_new and Node_destroy.
Node *Node_new() {
Node *node = malloc(sizeof(Node));
node->value = 0;
node->next = NULL;
node->previous = NULL;
return node;
}
void Node_destroy( Node *node ) {
free(node);
}
Node_destroy might seem silly, but it frees you (or anyone else) from having to remember how to destroy a Node. And it lets you change the internal structure of Node without changing the rest of the code (which happened while writing this).
You're using globals. Globals make everything more complicated and restrict what you can do with the code. Instead, wrap things like head, tail, and size into its own structure and pass that around.
typedef struct {
Node *head;
Node *tail;
size_t size;
} LinkedList;
And it needs its own create and destroy functions.
LinkedList *LinkedList_new() {
LinkedList *list = malloc(sizeof(LinkedList));
list->head = NULL;
list->tail = NULL;
list->size = 0;
return list;
}
void LinkedList_destroy( LinkedList *list ) {
for( Node *node = list->head; node != NULL; node = node->next ) {
Node_destroy(list->head);
}
free(list);
}
Note that LinkedList_destroy takes responsibility for cleaning up all its nodes, its one less thing for the user of LinkedList to worry about and potentially screw up.
LinkedList_destroy can call Node_destroy without knowing anything about how Node works. This is how we immediately benefit from the encapsulation and abstraction of Node. But don't use recursion, the list can be arbitrarily long and recursion risks a stack overflow.
Now we can write push and pop assured that things are properly created and destroyed. Note that they take a LinkedList rather than using globals.
void LinkedList_push(LinkedList *list, int value)
{
Node *node = Node_new();
node->value = value;
switch( list->size ) {
/* The list is empty, this is the first node */
case 0:
list->head = list->tail = node;
break;
default:
list->tail->next = node;
node->previous = list->tail;
list->tail = node;
break;
}
list->size++;
}
int LinkedList_pop( LinkedList *list ) {
Node *popped = list->tail;
switch( list->size ) {
/* The list is empty, nothing to pop */
case 0:
fprintf(stderr, "LinkedList was empty when popped.\n");
exit(1);
break;
/* Popped the last node */
case 1:
list->head = list->tail = NULL;
break;
/* Only one node left, it's both the head and tail */
case 2:
list->tail = list->head;
list->tail->previous = list->tail->next = NULL;
break;
default:
list->tail = popped->previous;
list->tail->next = NULL;
break;
}
/* Have to do this at the end because size_t is unsigned
it can't go negative */
list->size--;
int value = popped->value;
Node_destroy(popped);
return value;
}
I've used a switch so I can clearly demarcate all the special cases.
I'm not saying this is the best implementation of push and pop, or that it's even bug free, but they can be written without worrying about whether the structs have been properly initialized or freed. You can focus on the logic, not the memory management.
And then to demonstrate it all works...
void LinkedList_print( LinkedList *list ) {
for( Node *node = list->head; node != NULL; node = node->next) {
printf("%d\n", node->value);
}
}
int main() {
LinkedList *list = LinkedList_new();
for( int i = 0; i < 3; i++ ) {
LinkedList_push(list, i);
}
while( list->size != 0 ) {
printf("list->size: %zu\n", list->size);
LinkedList_print(list);
LinkedList_pop(list);
}
LinkedList_destroy(list);
}
$ ./test
list->size: 3
0
1
2
list->size: 2
0
1
list->size: 1
0

Basic C linked list syntax error

Note: I already got the intended function to work with my own code, but I saw a tutorial on another website and am wondering why it doesn't work.
https://www.eskimo.com/~scs/cclass/int/sx8.html
The premise is as follows:
I'm playing around with a very basic linked list:
typedef struct node {
int val;
struct node * next;
} node_t;
I am trying to have a function remove an entry by value. It is as follows:
int remove_by_value(node_t ** head, int val) {
for(head = &node_t; *head != NULL; head = &(*head)->next){
if ((*head)->val == val) {
*head = (*head)->next;
break;
}
}
}
However, I'm getting an error when calling this function, namely:
"prog.c:35:17: error: expected expression before 'node_t'
for(head = &node_t; *head != NULL; head = &(*head)->next){
^"
Any ideas? Is this just a simple syntax error that I'm not seeing? Thanks!
the root of the problem is that node_t is a type, not a variable and cannot take the address of a type.
The following code cleanly compiles.
be sure to check the logic,
for first iteration of the loop when head = NULL or only one struct in linked list
check logic for when desired struct is either last or next to last in the linked list
here is the code:
typedef struct node
{
int val;
struct node * next;
} node_t;
int remove_by_value(node_t ** head, int val)
{
int retVal = -1; // initialize to failed
node_t *previousNode = *head;
node_t *currentNode = *head;
for(;
previousNode && currentNode; // assure something to test
previousNode = currentNode, // update the pointers
currentNode = currentNode->next )
{
if (currentNode->val == val)
{
previousNode->next = currentNode->next;
retVal = 0; // indicate success
break;
}
}
return retVal;
} // end function: remove_by_value
Since I cannot comment on the accepted answer written by #user3629249:
That code is even worse than the original (except that it would compile).
I'd suggest something like this:
node_t *remove_by_value(node_t **head, int val)
{
node_t *ret = NULL;
for (; *head; head = &((*head)->next))
{
if ((*head)->val == val)
{
ret = *head;
*head = (*head)->next;
break;
}
}
return ret;
}
This code correctly removes the element from the beginning, middle and end of the list. In addition it gives the caller the chance to free the unlinked node.
1) The error you are getting was pointed out by iharob already.
2) I can understand that out of this head =&t_node you want head to point at the head of your list. a static variable may be needed in your file to be able to use this then you can point head correctly

How can I print the values in my linked list?

I have, for a large portion of the day, been trying to write a simple program with linked lists. My main issue seems to be not understanding why the memory I am accessing is not what I think it is. I am printf crazy and outputting every possible form of data I can and still am having trouble understanding why it will not work.
For example when I pass the &head to a function which takes node **location and I want to check whether the value inside location (and therefore head) is NULL or not, should I use if(!*location) return; or should I use if(!location) return;, It seems the later is correct, but why?
And when I want to create a node *current inside a function to keep track of things, should I start with node* current = *head or node* current = head, and most importantly, why? I have noticed that the later is better, but I cannot make sense of it still. Warnings go away when I typecast the statements, but it seems to fix nothing.
Here is some functions I have been writing, can you please give me hints on where I am not making sense in the code. Preferably, I hope to understand why the output seems to be a memory location and then accessing bad memory.
#include <stdio.h>
#include <stdlib.h>
typedef struct node_struct
{
int val;
struct node *next;
} node;
node* return_create_neck(node **head, int value)
{
node* ptr;
*head = ptr = (node *)malloc(sizeof(node));
(*head)->val = value;
(*head)->next = NULL;
return ptr;
}
node* return_append_tail(node **location, int value)
{
node* ptr;
*location = ptr = (node *)malloc(sizeof(node));
(*location)->val = value;
(*location)->next = NULL;
return ptr;
}
void print_linked_list(node **head)
{
if(!head)
return;
node *current = head;
while(current)
{
printf("%d ", current->val);
current = current->next;
}
printf("\n");
return;
}
int main(void)
{
node *head=NULL, *current=NULL;
int i=0;
for( current = return_create_neck(&head, 1);
i < 4;
current = return_append_tail(&current, i+1))
{ ++i; }
printf("Pritning...\n");
print_linked_list(&head);
return 0;
}
Your return_append_tail function doesn't actually append anything, unless called with the correct location, which you do not.
You should call it with &current->next from the main function.

Resources