C Linked List - Create Nodes within Memory Block - c

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..

Related

Freeing the previous node in a linked-list

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.

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.

difference between (*head)->next and &(*head)->next in C [LINKED LIST]

typedef struct n {
int data;
struct n *next;
}node;
This function deletes all nodes with odd values (without freeing memory or additional variables) :
void deleteOdds (node **head) {
if (*head == NULL) {
return;
}
while ((*head)->next) {
if ((*head)->data % 2 != 0) {
*head = (*head)->next;
} else head = &(*head)->next;
}
}
I understand the logic (and already have some assumptions), but I'm not sure how to explain the difference between
*head =(*head)->next;
and
head = &(*head)->next;
thanks in advance!
The key to understanding what's going on is to see what is being assigned in each case:
*head = (*head)->next; assigns to whatever head is pointing to, which is either the original head pointer, the pointer to which is passed to the function, or a next of some prior node. We modify what is pointed to, while the pointer stays the same. This amounts to deleting from the list (and creating a memory leak in the process).
head = &(*head)->next assigns to the head itself, i.e. it modifies the pointer, not the thing that it points to. This amounts to skipping ahead in the list, without modifying it.
Note: head is not the ideal name for the variable. Since the variable points to head pointer only until the first "skip", a better name for it would be current.

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.

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