Freeing the previous node in a linked-list - c

I wrote a function to traverse a linked-list, find the node with the smallest value for 'int frequency' and remove that node, once it had traversed the entire list. My error is coming from the node: 'prev'. When I run the code, I get an error saying: "prev is a null pointer". However, if i remove the '= NULL' part from the node declaration, I get a compilation error that says: "prev is uninitialized".
Is this because I need to assign/point prev to one of the existing nodes in the list? If so how would I point it the the node prior to the one I want to delete? (I thought that was done the way it is in my code but obviously not.)
Structure definitions:
struct LetterFrequencyPair
{
char character;
int frequency;
//Creating a pointer to point to the next child in the list
struct BinaryTreeNode* next;
};
struct BinaryTreeNode
{
//create a pointer to point to the LetterFrequencyPair
struct LetterFrequencyPair* letter_frequency_pair;
//create pointers to the children of the node
struct BinaryTreeNode* leftChild;
struct BinaryTreeNode* rightChild;
};
struct BinaryTreeNode* ret_lowestF()
{
int val = 1000;
struct LetterFrequencyPair* temp;
struct LetterFrequencyPair* temp1 = NULL;
struct LetterFrequencyPair* prev = NULL;
struct LetterFrequencyPair* low = malloc(sizeof(struct
LetterFrequencyPair));
struct BinaryTreeNode* lowest = malloc(sizeof(struct BinaryTreeNode));
temp = root;
if (temp == NULL)
{
printf("List is empty.\n");
}
else
{
while (temp != NULL)
{
printf("%c\t%d\n", temp->character, temp->frequency);
if (val >> temp->frequency)
{
low = temp;
lowest->letter_frequency_pair = low;
val = low->frequency;
temp1 = temp;
prev->next = temp1;
}
temp = temp->next;
}
}
prev->next = temp1->next;
temp1->next = NULL;
free(temp1);
printf("lowest frequency node is: %c\t%d\n", low->character, low-
>frequency);
return lowest;
}

When I run the code, I get an error saying: "prev is a null pointer". However, if i remove the '= NULL' part from the node declaration, I get a compilation error that says: "prev is uninitialized".
Well, yes. Although in a couple of places you attempt to assign to prev->next, nowhere do you assign to prev itself, unless you want to count its initializer. When that variable is NULL, or when it has no defined value at all, it does not point to any object. Under those circumstances, there is no prev->next.
It looks like you want to use prev to track the node preceding the current minimum. That presents a bit of a problem when the head node of your list is the current minimum. That can be worked around by setting prev to NULL in that case and writing extra code for that special case, but it's easier and cleaner to sidestep the problem by introducing an artificial predecessor:
struct LetterFrequencyPair head = { .next = root };
struct LetterFrequencyPair *prev = &head;
Note that there is no need to allocate the head node dynamically. For that matter, you should not need any dynamic allocations. Presently, your code leaks the memory it allocates for low initially to point to, and the allocation for lowest and freeing of the original lowest node is wasteful.
It is possible that the node you end up removing turns our to be the first one. You do not need special handling for that at the point of the deletion; it should fall out naturally in that case that head.next is set (via prev) to point to the new first node. At the end, however, you'll want to copy it back out:
root = head.next;
If the first node is not the one that was removed, then that assignment has no net effect.
You have a lot of other issues with your code, well beyond the scope of the question, but that should get you started.

Related

Add node to multilevel linked list

I apologize for this noob question but I'm really struggling with this. I have a linked list with two different links: Next and sort. I want to add to the front with next but add in increasing order to sort. However whenever I try and add to sort I get a segfault and I'm not sure how I'm supposed to access them in a way that they act as two distinct linked lists with the same data but in a different order.
this is my list:
typedef struct NODE {
value_t value;
key_t key;
struct NODE * next;
struct NODE * sort;
} Node;
and this is how I'm trying to write to it
Node * add_sorted(Node ** head, int value, key_t key){
Node *new_node = malloc(sizeof(Node));
if (new_node != NULL) {
new_node->sort->value = value;
new_node->sort->key = key;
new_node->sort = *head;
*head = new_node;
}
return new_node;
}
thank you in advance for enduring my ignorance
You are trying to assign value to somewhere that you did not initialize in address space.
new_node->sort->value = value;
new_node->sort->key = key;
In this part, you did not create *sort but it is declared like it is there. Creating new_node does not mean there is a *sort. Because it is a pointer. In this case, it does not point anywhere but void.
I do not know if it is related to your problem but I think you need to solve that one too.
Hope it helps.
In adding, new_node->sort = *head; doesn't have any sense. Because, previous setting of attribute sort will be erased.
first you are assigning value to a pointer without allocating memory for it or initializing it.
this should work:
if (new_node != NULL) {
new_node->sort = malloc(sizeof(Node));
new_node->sort->value = value;
new_node->sort->key = key;
new_node->sort = *head;
*head = new_node;
}
also note that your function argument int value has different type from the member you are assigning it to.(value_t value)
also I think here new_node->sort = *head; you meant new_node->next = *head; ,because with this assignment data ,you assigned to fields of sort will be lost.

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.

Adding an element to the end of an ordered list

I have just started learning about dynamic structures in C.
The first type that I'm trying to learn is the ordered list. I have created a few functions - namely, adding nodes to the beginning of the list and printing elements in the list, but then I decided to write a function that allows me to add elements to the end of the list. My function looks like this:
typedef struct Node* Node;
void add_end(Node *head, int value) {
Node new;
new = malloc(sizeof(struct Node));
new -> value = value;
new -> next = NULL;
if(*head == NULL) {
*head = new;
}
else {
Node help = *head;
while(help->next != NULL) {
help = help->next;
}
help->next = new;
}
}
Some clarification: My structure consists of two fields - value (int) and next (pointer to the next node).
Thus, my questions are:
1) Notice my substitution using the variable called "help" - when I tried to do this without it,namely writing *head wherever help appears now, and this function did not work properly - it only added as much as two elements. For example, if I pushed 1, 3, 5, 7 to the list, only 5 and 7 would be added. Why was this so? I really can't see any reasonable explanation.
2) At first I tried passing an argument of the type Node to the function (Not Node* as I'm doing now) and the function did not put anything to the list. Once again, I can't see the difference between writing using the type Node and writing the variable without the asterisk. Could you explain it to me in layman terms?
I know that my question may have a trivial answer, but please, be understanding - this is my first encounter with pointers and they may be quite complex to comprehend.
To try to answer your two questions:
Because you typedefed Node as typedef struct Node* Node; what you are passing into add_end as the first parameter is a double pointer to struct Node (like struct Node**). You then dereference it in your while loop with Node help = *head; This means that the value of the actual pointer is going to change. If you didn't have the help pointer, you would then keep moving head until you get to the end of the list. This means that you would only have two elements in the list -- the head itself and its next element.
The answer to this has to do with your typedef again. When you pass Node, with your typedef, you are only passing a single pointer to struct Node representing head, which means that dereferencing it will not give you the pointer to head, but the structure itself, which means that neither your if or else statements will work as intended, as your intent is to compare pointers.
Your function type should probably be:
void add_end(Node **head, int value) {
^
because head is a pointer to Node
Like:
void add_end(Node **head, int value) {
Node* new; // NOTICE Node*
new = malloc(sizeof(struct Node));
// TODO - add check for new being NULL
new -> value = value;
new -> next = NULL;
if(*head == NULL) {
*head = new;
}
else {
Node help = *head;
while(help->next != NULL) {
help = help->next;
}
help->next = new;
}
}
and call it like:
Node* head = NULL;
add_end(&head, 42);
add_end(&head, 42);
add_end(&head, 42);

C Linked List - Create Nodes within Memory Block

I am attempting to create a linked list within a pre-allocated block of memory. Put simply,
I have an initial memory pool declared like so.
void *block = malloc(1000);
I created the head of a linked list from this pool:
struct node *root = block;
Let's say the memory address of the initial block is 100000. If I add a single linked list node of size 100, this just starts at 100000 (as it is the first node, sharing the memory address of the first block). If I add a second node of size 200, this should start at 100100 (at the end of the last block). And the next one would start at 100300, and so on.
My method to add nodes to the list can be condensed as follows:
void add_node(int size) {
new_node = malloc(sizeof(struct node));
struct node *current = root;
while (current != NULL) { //loop to get to the end of the linked list
...stuff (irrelevant to this question)...
current = current->next;
}
new_node->value = size;
current->next = new_node;
}
The node definition is pretty generic:
struct node {
int value;
int free;
struct node *next;
};
The main method is as follows:
int main(void) {
create_block(1000);
add_node(100);
add_node(200);
print_all();
}
And print_all simply iterates through and prints out the memory addresses:
void print_all() {
printf("%ld (block start)\n", block);
struct node* current = root;
while (current != NULL) {
printf("%ld (%d)", current->value);
current = current->next;
}
}
However, when adding nodes with values 100 and 200, the memory addresses are as follows:
25770205072 (block start)
25770205072 (location of 100 node - this is ok)
25769968848 (location of 200 node - not ok. This should be 25770205072 + 100)
25770204784 (location of remaining 700 memory node - not ok. This should be 25770205072 + 100 + 200)
Any clues for what I'm doing wrong? I tried a few different approaches without any luck.
Thanks for your time! I appreciate any assistance very much.
Actually, this was so simple. I literally used what #Ajay Brahmakshatriya mentioned - "You could keep a counter that starts at root and is incremented by size" - and created a new long that starts at the root, and just increment it by size every time. Instead of setting the new node to a new blob of allocated memory, I just set it to the old address + size (and incremented the address appropriately). Works fine now! Thanks!
Caveat: Since you didn't show your pool code, this may not be a complete solution, but add_node has some bugs.
You have to set new_node->next = NULL
At the end of the loop, current could be null unless "stuff irrelevant ..." guarantees non-null. In which case, it's not irrelevant.
You need an extra variable (e.g. prev)
Also note that because add_node is calling malloc, it's bypassing your memory pool, so you can't have too many expectations on addresses.
Here's what I think you need [please pardon the gratuitous style cleanup]:
void
add_node(int size)
{
struct node *new_node = malloc(sizeof(struct node));
struct node *current;
struct node *prev;
// loop to get to the end of the linked list
prev = root;
for (current = root; current != NULL; current = current->next) {
// ... stuff(irrelevant to this question) ...
prev = current;
}
new_node->value = size;
new_node->next = NULL;
// NOTE: this assumes root is _always_ non-null
#if 0
prev->next = new_node;
#else
if (prev != NULL)
prev->next = new_node;
else
root = new_node;
#endif
}
I just try remember about the "linked list" concept...
It different from array which each elemen of array located perfectly next/adjacent from the elemen before, in the memory.
I think each node "dynamically allocated". Program try to find the still free memory and the certain present node can be located far away from previous node. But the size block which node was allocated is depend on how we write malloc code, size to allocate it.
So I think, nothing wrong with your codes..

Segfault when accessing next node in singly linked list

I'm trying to just reverse a singly linked list, but with a bit of a twist. Rather than having the pointer to the next node be the actual next node, it points to the pointer in that next node.
struct _Node
{
union
{
int n;
char c;
} val;
void *ptr; /* points to ptr variable in next node, not beginning */
int var;
};
typedef struct _Node Node;
I know how to reverse a normal singly linked list and I think I have the general idea of how to go about solving this one, but I'm getting a segfault when I'm trying to access head->ptrand I don't know why.
Node *reverse(Node *head)
{
Node * temp;
Node * prev = NULL;
while(head != NULL)
{
temp = head->ptr + 4; /* add 4 to pass union and get beginning of next node */
head->ptr = prev;
prev = head;
head = temp;
}
return prev;
}
Even if I try and access head->ptr without adding 4, I get a segfault.
The driver that I have for this code is only an object file, so I can't see how things are being called or anything of the sort. I'm either missing something blatantly obvious or there is an issue in the driver.
First, I'll show you a major problem in your code:
while (head) // is shorter than while(head != NULL)
{
// Where does the 4 come from?
// And even if: You have to substract it.
// so, definitively a bug:
// temp = head->ptr + 4; /* add 4 to pass union and get beginning of next node */
size_t offset_ptr = (char*)head->ptr - (char*)head;
// the line above should be moved out of the while loop.
temp = head->ptr - offset_ptr;
Anyways, your algorithm probably won't work as written. If you want to reverse stuff, you are gonna have to work backwards (which is non-trivial in single linked lists). There are two options:
count the elements, allocate an array, remember the pointers in that array and then reassign the next pointers.
create a temporary double linked list (actually you only need another single reversely linked list, because both lists together form a double linked list). Then walk again to copy the next pointer from your temporary list to the old list. Remember to free the temporary list prior to returning.
I tried your code and did some tweaking, well in my opinion your code had some logical error. Your pointers were overwritten again and again (jumping from one node to another and back: 1->2 , 2->1) which were leading to suspected memory leaks. Here, a working version of your code...
Node *reverse(Node *head)
{
Node *temp = 0;
//Re-ordering of your assignment statements
while (head) //No need for explicit head != NULL
{
//Here this line ensures that pointers are not overwritten
Node *next = (Node *)head->ptr; //Type casting from void * to Node *
head->ptr = temp;
temp = head;
head = next;
}
return temp;
}

Resources