Struct node next pointer - c

I have a struct which holds a next pointer to a struct of the same type.
I have the following code which stores the number of the struct that the node pointer stores, frees it, and sets node to the node->next. How is node->next known what it is if free has been called right before it?
double data = node->element;
free(node)
node = node->next;
return data;
However, following the same logic, the following code segfaults. The only difference is that this uses a node and the other just stores the element in its basic data type.
struct node *temp;
temp = node;
free(node);
node = node->next;
return temp;
Why is this happening? And how does the first segment of code work anyway?
Thanks

You shouldn't ever dereference a freed pointer - it is asking for trouble and is likely the source of your segfault. Even if it doesn't segfault, you cannot make any assumptions on the vailidity of the data at that memory address and so it is just not a good thing to do.
You haven't given too many details in your question but why not modify your code slightly to try this order instead
struct node *temp = node->next;
free(node);
return temp;
this way you store the pointer before deleting node and should not get a segfault
As for the first code
double data = node->element;
free(node)
node = node->next;
return data;
why not just replace with
double data = node->element;
free(node)
return data;
as this removes the code that accesses freed memory, and your line
node = node->next;
is practically pointless when in the next line you free node anyway.

Related

Double Linked List Crashing After Appending Node

I am trying to achieve a double linked list using C and have encountered a crash whenever I try to append a third node to my list. I have located the line in my code in which my program crashes, but I cannot understand why since the code looks "safe". I have received no warnings or errors from the compiler. If anyone is able to explain a possible pointer error or the reason behind the crash, it would be much appreciated. Any questions or concerns related to my code will be answered as soon as I see them.
struct node *appendNode(struct node *headRef, unsigned short int newData) {
struct node *newNode = (struct node*)malloc(sizeof(struct node*));
newNode->data = newData;
newNode->next = NULL;
if(headRef == NULL) { //list is empty and returns the newNode to become the head pointer
newNode->previous = NULL;
return newNode;
} else { //list is not empty and newNode is appended to end of list ----(Area of crash)----
struct node *current = headRef;
while(current->next != NULL) {
current = current->next;
}
current->next = newNode;
newNode->previous = current;
return headRef;
} //----------------------------------------------------------------------------------
};
The code presented above is a function that appends a new node to the list. It returns a new address or same address back when finished to update the head pointer used in 'main'. The code runs functionally whenever I append the first two nodes, but crashes whenever it tries to append a third node.
The amount of memory space you are allocating is the size of a pointer to a struct node, not the actual size of a struct node - which you want.
So it should be
struct node *newNode = (struct node*)malloc(sizeof(struct node));
As a consequence of allocating insufficient memory, your program is writing outside the memory block that it allocated, which causes undefined behavior. This means that anything can happen. For example, the program may crash immediately, not at all, or at a later time.

Understanding the logic behind building linked list using local reference

Below is the code for creation of linked list using local reference logic.
Not able to understand the code inside the for loop especially the 2nd line. (see // HERE)
Can somebody please elaborate how this logic is working.
void push(struct Node** head_ref, int new_data)
{
struct Node *newNode = (struct Node *)malloc(sizeof(struct Node));
newNode->data = new_data;
newNode->next = *head_ref;
*head_ref = newNode;
return;
}
struct Node* buildWithLocalRef()
{
int i=0;
struct Node *head = NULL;
struct Node **lastptrRef = &head;
for(i=1;i<6;i++)
{
push(lastptrRef,i);
lastptrRef = &((*lastptrRef)->next); // HERE
}
return head;
}
int main()
{
struct Node* head;
head = buildWithLocalRef();
printList(head);
return 0;
}
The technique you're seeing is building a linked list by forward-chaining. It is the most direct, and sensible way to build an ordered list from beginning to end, where the list does not have a tail pointer (and yours does not).
There are no "references" here. This isn't C++. This is using a pointer to pointer. The variable name is dreadfully named, btw. How it works is this:
Initially the list is empty, head is NULL
A pointer to pointer, lastptrRef will always hold the address of (not the address in; there is a difference) the next pointer to populate with a new dynamic node allocation. Initially that pointer-to-pointer holds the address of the head pointer, which is initially NULL (makes sense, that is where you would want the first node hung).
As you iterate the loop a new node is allocated in push . That node's next pointer is set to whatever value is in the pointer pointed to by lastptrRef (passed as head_ref in the function), then the pointer pointed to by lastptrRef is updated to the new node value.
Finally, lastptrRef is given the address of the next member in the node just added, and the process repeats.
In each case, lastptrRef hold the address of a pointer containing NULL on entry into push. This push function makes this harder to understand. (more on that later). Forward chaining is much easier to understand when done directly, and in this case, it would make it much, much easier to understand
struct Node* buildWithLocalRef()
{
struct Node *head = NULL;
struct Node **pp = &head;
for (int i = 1; i < 6; i++)
{
*pp = malloc(sizeof **pp);
(*pp)->data = i;
pp = &(*pp)->next;
}
*pp = NULL;
return head;
}
Here, pp always holds the address of the next pointer we'll populate with a new node allocation. Initially, it holds the address of head. As each node is inserted pp is set to the address of the next pointer within the latest node inserted, thereby giving you the ability to continue the chain on the next iteration. When the loop is done, pp holds the address of the next pointer in the last node in the list (or the address of head of nothing was inserted; consider what happens if we just pull the loop out entirely). We want that to be NULL to terminate the list, so the final *pp = NULL; is performed.
The code you posted does the same thing, but in a more convoluted manner because push was designed to push items into the front of a list (apparently). The function always sets the pointer pointed to by head_ref to the new node added, and the node's next is always set to the old value in *head_ref first. Therefor, one can build a stack by doing this:
struct Node* buildStack()
{
struct Node *head = NULL;
for (int i = 1; i < 6; i++)
push(&head, i);
return head;
}
Now if you print the resulting linked list, the number will be in reverse order of input. Indeed, push lives up to its name here. Dual-purposing it to build a forward-chained list is creative, I'll grant that, but in the end it makes it somewhat confusing.

C linked list can't properly add data to list

Bit of a lengthy question so please bear with me. I am trying to create a doubly linked list in C using a dummy node as the head. For whatever reason, however, the list only saves the last node I read into it, and links the prev node pointer and the next node pointer to that last node, so if I try and iterate over the list, it gets stuck in an infinite loop.
Here is my node header file and C file. The linked list implementation isn't meant to be a full linked list implementation, so I only included the functions I need:
node.h:
#ifndef _node_h
#define _node_h
#include "task_block.h"
#include <stdio.h>
typedef struct node {
task_block_type *data;
struct node *next;
struct node *prev;
}node_t;
node_t *node_new(task_block_type *data);
void add(node_t *new, node_t *head);
#endif
node.c:
#include "node.h"
#include "task_block.h"
#include <stdlib.h>
node_t *node_new(task_block_type *data) {
node_t *node = NULL;
node = malloc(sizeof(node_t));
node->data = data;
node->next = NULL;
node->prev = NULL;
return node;
}
void add(node_t *new, node_t *head) {
node_t *current = head;
if (head->next == NULL) {
head->next = new;
head->next->prev = head;
return;
}
while(current->next != NULL) {
current = current->next;
}
current->next = new;
current->next->prev = current;
return;
}
And finally, the code that is messing up from main.c:
while (j < numTasks) {
if (tasks[j].taskID == currentID) {
*newTask = *task_block_new(tasks[j].taskID, tasks[j].period);
newTask->startTime = starts[i];
newTask->deadline = deadlines[i];
newTask->executionTime = executions[i];
*nodeNew = *node_new(newTask);
add(nodeNew, eventQueue);
}
I have already tested that my new task_block_type get the correct data form the text file and that the new node I create is initialized properly with the task block. Once I read it into my list with add(), however, it messes up. Any help would be greatly appreciated as I've been trying to fix this problem for several hours now and still haven't found a solution
EDIT:
self contained example:
*node_new is meant to be a constructer for my node objects and is supposed to return a pointer to a node object. So for example, say instead of having a node which contains the task_block_type as above, I have one that contains an int. If I wanted to initialize it with a value of 5, I would call
*newNode = (node_t *)malloc(sizeof(node_t));
*newNode = *node_new(5);
Hope that helps
Change this:
*nodeNew = *node_new(newTask);
To this:
nodeNew = node_new(newTask);
Your original code copies the (dereferenced) value returned by node_new() to the value at (dereference of) *nodeNew. Thus, the pointer nodeNew never gets updated with the address of the new node created by node_new()... so you keep overwriting the value at *nodeNew while passing its unchanging address to add().
And you get a memory leak into the bargain. You are responsible for free()ing every pointer ever returned to you by malloc(). But here, for the same reason given above, you're not keeping copies of the returned pointers to enable this... just linking to nodeNew over and over again.
You need to update the pointer nodeNew with the location of, well, each new node, before passing it on to add(). Then you'll actually be linking different nodes, and at their original addresses, rather than copying them to the same address in a leaky fashion and linking it to itself, infinitely.
You also need to free() all memory that you have dynamically allocated once you're finished using it, e.g. through a sweep of the linked list in a 'destructor' function or at the end of your program. Otherwise you're leaking memory. This is a basic error and, even in cases where it doesn't stop a program from working, wastes users' RAM, which they rightly dislike!
I highly recommend studying pointers and dynamic allocation some more before continuing trying to write code like this.

malloc is doing strange things with memory addresses

I am doing a bucket sort, sorting pointers to nodes into a linked list with a dummy header. The user inputs the values for the nodes, and then they are immediately dropped into their appropriate bucket. My problem arises when I am attempting to allocate the memory for the second node (not including the dummy header) for the linked list.
This is the code used to drop the nodes into the bucket, var being the value that is being sorted, and current being the newly created node to be sorted:
void bucketSort(int var, nodeptr current)
if(!bucket[var])
{
buckets[var] = (nodeptr) malloc(sizeof(nodeptr));
buckets[var]->next = current;
bucketrear[var] = current;
}
else
{
bucketrear[var]->next = current;
bucketrear[var] = current;
}
}
This is a simplified version (less values) of the code used to create the new node:
void addNode(int value)
{
nodeptr newNode;
newNode= (nodeptr) malloc(sizeof(nodeptr));
newNode->value = value;
newNode->next = NULL;
bucketDrop(value, newNode);
}
With Trace Statements, I discovered that before the Malloc of the second Node (with same value as the first), the Address of the bucket[value]->next was a normal address, but afterwards the address was 17. This value of 17 came up in every single test that I did.
Any help or ideas would be great. Thankyou in advance.
I think this is one of solution.
buckets[var] = (nodeptr) malloc(sizeof(*buckets[var]));
newNode= (nodeptr) malloc(sizeof(*newNode));
You're mixing up the struct for your node and the pointer pointing to it and thus overwriting the memory location.
Since you're casting the malloc result to a nodeptr type it means that this is a pointer (since malloc() returns a pointer) which also means your're allocating memory the size of a pointer.
So given that you named your struct nodestruct, to allocate enough memory you'd write
newNode = (nodeptr) malloc(sizeof(struct nodestruct));
The following should resolve your issue:
newNode= (nodeptr) malloc(sizeof(*nodeptr));
The reason is nodeptr looks to be a pointer to struct, and hence you should allocate memory for the struct instead of a pointer.
From, next time please paste your struct definitions also.

Delete Linked List Function

Here's my function to delete a linked list:
void deleteList( NODE* head )
{
NODE* temp1;
NODE* tempNext;
temp1 = head;
tempNext = NULL;
while( temp1 != NULL )
{
tempNext = temp1->next;
free(temp1);
temp1 = tempNext;
}
}
So temp1 first points where the head pointer is pointing. If it isn't NULL, tempNext will be set to point to the next element of the list. Then the first element (temp1) is free'd, and temp1 is reassigned to point to where tempNext is pointing and process repeats.
Is this the right approach to deleting an entire list?
I ask this because when I print the list after using this function, it still prints the list. And IIRC freeing something doesn't delete it but only marks it as available so I'm not sure how to tell if this is correct or not.
Your code looks correct.
You're also correct that freeing a list's elements doesn't immediately change the memory they pointed to. It just returns the memory to the heap manager which may reallocate it in future.
If you want to make sure that client code doesn't continue to use a freed list, you could change deleteList to also NULL their NODE pointer:
void deleteList( NODE** head )
{
NODE* temp1 = *head;
/* your code as before */
*head = NULL;
}
It still print the list, because you probably don't set the head pointer to NULL after calling this function.
I ask this because when I print the list after using this function, it still prints the list.
There is a difference between freeing a pointer and invalidating a pointer. If you free your whole linked list and the head, it means that you no longer "own" the memory at the locations that head and all the next pointers point to. Thus you can't garintee what values will be there, or that the memory is valid.
However, the odds are pretty good that if you don't touch anything after freeing your linked list, you'll still be able to traverse it and print the values.
struct node{
int i;
struct node * next;
};
...
struct node * head = NULL;
head = malloc(sizeof(struct node));
head->i = 5;
head->next = NULL;
free(head);
printf("%d\n", head->i); // The odds are pretty good you'll see "5" here
You should always free your pointer, then directly set it to NULL because in the above code, while the comment is true. It's also dangerous to make any assumptions about how head will react/contain after you've called free().
This is a pretty old question, but maybe it'll help someone performing a search on the topic.
This is what I recently wrote to completely delete a singly-linked list. I see a lot of people who have heartburn over recursive algorithms involving large lists, for fear of running out of stack space. So here is an iterative version.
Just pass in the "head" pointer and the function takes care of the rest...
struct Node {
int i;
struct Node *next;
};
void DeleteList(struct Node *Head) {
struct Node *p_ptr;
p_ptr = Head;
while (p_ptr->next != NULL) {
p_ptr = p_ptr->next;
Head->next = p_ptr->next;
free(p_ptr);
p_ptr = Head;
}
free(p_ptr);
}

Resources