Add node to multilevel linked list - c

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.

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.

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);

Modified structs allocated in memory do not retain values after end of function

I have a couple of structs: A HashTable, which contains a table of pointers to WordNodes, and each WordNode contains a pointer to a List, which is a linked list made up of ListNodes.
I wrote a function to create a list and add list nodes to a WordNode:
int addWord(char* word, HashTable* hash_table, int id)
{
WordNode* current = calloc(1, sizeof(WordNode));
current = hash_table->table[hash];
// ...
if(current->docs == NULL){
// Create a new list, and initialize it
List* list = calloc(1, sizeof(List));
list->head = NULL;
list->tail = NULL;
int occur = 1;
ListNode* list_node = AddNode(list); // Create the first node in the list
current->docs = list; // Link the WordNode to the list
// Fill in relevant details of ListNode
list_node->id= &id;
list_node->occurrences = &occur;
list_node->next = NULL;
That is my function, but since it's been giving me trouble, I added a couple of lines inside it to test it:
printf("Testing:\n");
WordNode* wnode = calloc(1, sizeof(WordNode));
wnode = hash_table->table[hash];
List* my_list = calloc(1, sizeof(List));
my_list = wnode->docs;
ListNode* dnode = calloc(1, sizeof(ListNode));
dnode = my_list->head;
printf("Results: ocurrences: %d, id: %d\n",*((int*)dnode->occurrences),
*((int*)dnode->id));
printf("The dnode is %d\n", doc_node);
}
When called in main, the testing code inside the function produces the expected output:
Results: ocurrences: 1, id: 15
The dnode is 13867424
However, the same testing in the line immediately following the function call in main produces a weird output, even though the pointer seems to be pointing to the same address.
Results: ocurrences: 0, id: 54
The dnode is 13867424
Possibly relevant code from the function that adds a new node to the list:
ListNode* AddNode(List * list)
{
ListNode* node = calloc(1, sizeof(ListNode));
node->next = NULL;
if(list->tail == NULL){
list->head = node;
list->tail = node;
}
else{
list->tail->next = node;
list->tail = node;
}
return node;
}
I can't seem to figure out what I am doing wrong. It would seem to me that I am somehow handling the structs as local variables, even though I am allocating memory for them, which makes me think they shouldn't change after the function is done. It is probably a C-programmer's beginner mistake, but I can't seem to figure out where I am getting this wrong. Any help would be greatly appreciated.
One set of problems is in the code:
int addWord(char* word, HashTable* hash_table, int id)
{
…omitted…
int occur = 1;
ListNode* list_node = AddNode(list); // Create the first node in the list
current->docs = list; // Link the WordNode to the list
// Fill in relevant details of ListNode
list_node->id= &id;
list_node->occurrences = &occur;
You're storing a pointer to a parameter and a pointer to a local variable in your structure. Dereferencing either of those after the function returns is undefined behaviour. The space occupied by those could be reused by the compiler for any purpose at any time; they could become completely invalid (but probably won't).
Why do you have pointers in your structure for those two items? Surely, the structure should just contain a couple of int members, not int * members!
If, perchance, your code is compiling with warnings, don't submit it to SO; fix the warnings first. Or seek help on how to resolve the compiler warnings. They all matter. At this stage in your career, remember that the compiler knows a lot more about C than you do. If it warns about something in your code, the compiler is probably correct to be worried and the code is probably incorrect in some way.
Your code doesn't show where word is used — it could be that you are not copying that data either.

What is the idiomatic way to write prepend for a linked list?

I've started writing a linked-list implementation in C:
typedef struct node node;
struct node {
node *next;
int value;
};
I can easily write the function append which attaches a new node to the end of the list:
void append(node *head, int value) {
if (! head->next) {
head->next = malloc(sizeof(node));
head->next->value = value;
return;
}
append(head->next, value);
}
If I were writing prepend using a functional language like Scheme, the obvious thing to do would be to simply return a new node with "next" pointing to the previous head:
(define (prepend head value)
(cons value head))
and I can write this easily in C:
node *prepend(node *old_head, int value) {
node* head = malloc(sizeof(node));
head->value = value;
head->next = old_head;
return head;
}
but now my append function doesn't return anything and just mutates the list, while my prepend function does return something and doesn't mutate the original list. This is a side effect of how linked lists are implemented, but it feels wrong.
One solution might be to rewrite prepend to add a new node, but use the previous head for the new value and the new node for the old head's value...
void prepend(node *head, int value) {
node* new = malloc(sizeof(node));
memcpy(new, head, sizeof(node));
head->next = new;
head->value = value;
}
but something about this also feels wrong.
Another solution might be to represent a list not as the head node, but as a different struct which points to the head node:
typedef struct list list;
struct list {
node *head;
};
Now my prepend function can just change where list->head points, and it doesn't have to return anything. This feels the cleanest, but it introduces additional baggage; now I have to use a helper function for append and many other functions (or implement them differently).
What's the idiomatic way to implement this function in C?
Note: I'm new to C and would appreciate any comments regarding my code style or correctness, even if unrelated to the question.
There's no "right" answer in my opinion, but I like the struct list approach. You can even make it opaque to the end user. Then you could change the underlying implementation to be a doubly linked list or backed by array, and the user wouldn't have to change any code to take advantage of it.

Adding nodes to a global linked-list

I am attempting to construct my first linked list, and having read a basic introduction, have done the following. Firstly, declare a linked list node as:
struct errorNode {
uint8 error;
struct errorNode* next;
};
Secondly, define the first node globally as:
struct errorNode errorList = {0, NULL};
This has been done to allow each of the libraries that make up my current project to insert errors into a common list. The function to do this is:
void errorListWrite(uint8 error) {
struct errorNode* newNode = malloc(sizeof(struct errorNode));
newNode->error = error;
newNode->next = &errorList;
errorList = *newNode;
}
Whilst this compiles without error, it does not function as expected. I thnk the problem is with the last two statements of the list write function, but I am unsure. A hint as to what I am doing wrong would be most appreciated.
The problem is that you create a circular list.
newNode->next = &errorList;
So newNode links to the global node.
errorList = *newNode;
This is equivalent to errorList.error = newNode->error; errorList.next = newNode->next;.
So now errorList links to the global node. Oops.
What you could do instead, is insert the new node after the global node in the list:
newNode->next = errorList.next;
errorList.next = newNode;
This is assuming that you want a global node at all. If you don't, then you could start with struct errorNode *errorList = 0;, and add a new node like this:
newNode->next = errorList;
errorList = newNode;
When you come to use the list, your list-traversal may look a little different. With a global pointer-to-node you'll start with a pointer to the first node, that you must check for null before using. With a global node you'd start with a node that definitely exists, but whose next pointer might be null.
Well, the problem is with the last line: you are just overwriting the data in in the old error node!
What you probably need is to have the head (pointer to the first node) globally accessible, not the first node itself. This way you don't need a fake entry in your list.
(Be warned that your code is not thread-safe.)
Code:
errorNode* pGlobalErrorList = NULL;
// in errorListWrite
newNode->next = pGlobalErrorList;
pGlobalErrorList = newNode;
Your head (errorList) should be a pointer and should be initialized to NULL unless you have a need for the initial entry of a node with a value of 0:
struct errorNode* errorList = NULL;
Then your function needs to reassign errorList properly.
void errorListWrite(uint8 error) {
struct errorNode* newNode = malloc(sizeof(struct errorNode));
newNode->error = error;
newNode->next = errorList;
errorList = newNode;
}
This is all assuming your new node will be the new head of the list, and not the new tail.
errorList should be a pointer to first node (not the first node)
also you need to know what is the last node this will be modified
the head of list will not be modified, it will be used only when you want to travel from the beginning of list.

Resources