I'm new to C so be patient with me if you see some really newbie error in my code!
As part of a homework, I need to create an ordered list in order to store some data. What I've done so far is to create the struct which will represent each node of the list (firstNode is a global variable that points to the first node of the list):
typedef struct Node {
struct Node *next;
int id;
int value;
}Node;
Node *firstNode = NULL;
After that I created a function that inserts a new node into the list by checking the values of the nodes. Nodes with smaller values should be before others. So what I did was this:
void addNewNode(int nodeId, int nodeValue) {
Node *newNode = (Node*) malloc(sizeof(Node));
Node *temp, *tempPrev;
newNode->id = nodeId;
newNode->value = nodeValue;
if(firstNode == NULL) {
newNode->next = firstNode;
firstNode = newNode;
}
temp = firstNode;
tempPrev = NULL;
while(temp->value < newNode->value) {
tempPrev = temp;
temp = temp->next;
}
if(tempPrev == NULL) {
newNode->next = firstNode;
firstNode = newNode;
}
else {
tempPrev->next = newNode;
newNode->next = temp;
}
}
The problem with the code above is that sometimes the program crashes, but I can't find the error!
Also, what I'm trying to do next is, if some nodes have the same value, then they are ordered according to their id (nodes with smaller IDs come first). How can I do this? I'm really confused!
The program crashes because in the while loop condition, you don't check whether the temp equals to NULL. In other words, if you try to insert a new node with a greater value than all the others already inside the list, temp reaches to the end of the list (so temp equals to NULL) and you try to get the value of that node! So a fix would be:
while(temp!=NULL && temp->value>newNode->value)
{
....
}
As for the id of the nodes, you could extend your while loop condition like this:
while(temp!=NULL && (temp->value<newNode->value || (temp->value==newNode->value && temp->id<newNode->id))
{
....
}
Also, the first if-statement, where you check whether firstNode is NULL, is not necessary in your case. If it's NULL, the program will not get into the while-loop and will go directly to the first if-statement after the while-loop.
By the way, nice code for a new programmer in C :-)
1.
if(firstNode == NULL) {
newNode->next = firstNode;
firstNode = newNode;
return; // done with inserting the first node...need not continue.
}
2.
// ensure temp is not null only then access its value.
while(temp && (temp->value < nodeId->value)) {
tempPrev = temp;
temp = temp->next;
}
for one thing, you have nodeID -> value in there. NodeID is an int, so this won't work.
As a method of debugging, I would create a simple test harness. In other words, a test program you write that runs through all the common scenarios you can think of (aka unit testing). Each unit test checks to ensure that it runs properly and generates the expected output. This way, you can be confident that if everything checks ok, you're good to go. If a unit test fails, you know exactly what's broken.
I would suggest constructing a couple of test cases that check your boundary conditions (e.g. add an element to an empty list; add an element that should end up at the front of an existing list; add an element that shold end up at the end of an existing list; add an element that should end up somewhere in the middle of an existing list). Make a "print" function that will print the elements in the list to the console for debug. That will at least help you narrow down what the context of your crash is. One possibility (I don't know how many adds you're doing) is that the program runs out of memory and malloc fails. You can check for that because I think malloc returns NULL if it fails to allocate the requisite memory.
Node *newNode = (Node*) malloc(sizeof(Node));
if(newNode == NULL)
{
printf("Out of Memory!");
return;
}
Related
I'm working on my final project and I was introduced to linked lists, which I must use.
I'm incredibly frustrated after trying to understand how the code works. The concept to me makes complete sense. The code i'm given as an example though, doesn't.
typedef struct node_s {
char name[20];
int age;
struct node_s *listp;
} node;
while (!feof(inp)) {
temp = (node *)malloc(sizeof(node)); // creation of memory
fscanf(inp, "%s%d", temp->name, &temp->age);
if (head == NULL)
head = temp; // setting the head of the list
else {
tail->listp = temp; // else connecting to previous element
}
tail = temp; // updating the current element
tail->listp = NULL; // setting pointer to null.
}
I'm confused at how tail->listp will point to the second element, when each time it's set to be NULL. To further illustrate my confusion, in the else statement tail->listp will point to the new element, which is understandable.
But at the end we point tail->listp to NULL which just disregard the else statement. Yet the code works just fine, and here I am, extremely confused.
You're missing the statement before, which is
tail = temp; // updating the current element
In a loop, you create a new element temp, and link it onto the list. If it's the first element, you start the list by setting it to both the head and the tail, essentially. If it's not the first element, you link it onto the end of the list.
tail->listp = temp;
Then, you set tail=temp to update the pointer to the end of the list, and make sure that the element at the end of the list is pointing to null
tail->listp = NULL;
You could also do
temp->listp = NULL;
tail=temp;
which would be equivalent, if my eyes don't fail me.
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.
This function get a pointer to the "Dummy" item of the list (1st item) and a struct typed "Node" to add...
But it goes into an infinite loop... whats wrong???
void listAdd(Node* dummy, Node tmpNode) {
Node* toAdd = (Node*)malloc(sizeof(Node));
*toAdd = tmpNode;
Node *tmp1,*tmp2;
tmp1 = dummy;
tmp2 = (*dummy).next;
while (tmp1 != NULL){
if ( ((*tmp1).info.id < (*toAdd).info.id && (*tmp2).info.id > (*toAdd).info.id ) || (tmp2==NULL) ) {
(*toAdd).next = (*tmp1).next;
(*tmp1).next = toAdd;
return;
}
tmp1 = (*tmp1).next;
tmp2 = (*tmp2).next;
}
}
EDIT:
I got a bit carried away with this (it's a slow day at work) so I rewrote the function to use (IMHO) clearer variable names, fewer redundant variables, and added basic error handling. The example below supports insertion whereas the previous example assumed simple appending to the end of a list which was the result of not reading the question properly (see edits if you're curious).
void listAdd(Node* currentNode, Node toAdd)
{
Node * newNode = malloc(sizeof(Node));
if(!newNode){
//ERROR HANDLING
}
* newNode = toAdd;
newNode->next = NULL;
while (currentNode)
{
if(!currentNode->next)
//We've got to the end of the list without finding a place to insert the node.
//NULL pointer always evaluates to false in C regardless of the underlying value.
{
currentNode->next = newNode;
return;
}
//Test each member of the list to find out whether to insert or skip.
if((newNode->info.id > currentNode->info.id) && (newNode->info.id <= currentNode->next->info.id) ){
newNode->next = currentNode->next;
currentNode->next = newNode;
return;
}
else currentNode = currentNode->next;
}
}
As has been mentioned in previous posts. dereferencing a pointer to a struct member uses the rather pretty -> notation which has a rather good imagery to it. Note also, that NULL will always evaluate as false, and that unless you want some bad things to happen (at best a segfault, at worst some takes over your machine) you need to make sure you're writing into proper memory areas, so you must always check that malloc returns !NULL.
note: In C, never cast the return value of a malloc() call as this can mask strange and dangerous behaviours. In C++, you must cast the result, so you need to think about who you're going to offend if you expect your program to compile as valid C and C++. See Do I cast the result of malloc? for details.
I am trying to understand the code of linked lists. I understand how they work.
I am looking at some code to do with dynamic memory and linked lists, I have simplified it here:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
char *word;
struct node *next;
} node;
void display_word(node *start) {
node *start_node = start;
puts("");
for(; start_node != NULL; start_node = start_node->next) {
printf("%s", start_node->word);
}
}
node* create_node(char *input) {
node *n = malloc(sizeof(node));;
n->word = strdup(input);
n->next = NULL;
return n;
}
int main() {
node *start_node = NULL;
node *n = NULL;
node *next_node = NULL;
char word_holder[20];
for(; fgets(word_holder,80,stdin) != NULL; n = next_node) {
next_node = create_node(word_holder);
if(start_node == NULL)
start_node = next_node;
if(n != NULL)
n->next = next_node;
}
display_word(start);
}
So the program creates a linked list of each word the user enters and then it prints it out.
What I dont understand is in the main() function where next_node is assigned to a new node everytime to create a new one, but start_node points to next_node, so it will point to every new node that next_node creates each time? So how is it possible to still keep the list? Shouldn't we lose the old node each time?
Can someone explain please.
When the first node is created, a pointer to it is saved in start.
When subsequent nodes are created, they are added at the end of the list, so start still points to the first node, and through it, the rest of the list.
Step through the code with a debugger, or get out a pencil and paper and draw what's happening as you step through in your brain, and you'll see how it all gets put together.
When the first node is created, a pointer to it is saved in start.
After every iteration of the loop, "n" is set to the node just created, because the last piece of the for loop (;n = next) is executed after every iteration of the loop. So mid loop execution "n" will always be pointing to the previous node. Therefore the statement n->next = next is setting the previous node's "next" pointer to the new node.
So during the second iteration of the loop, n = start, and start->next is set to "next" the node you just created.
I hope this answers your question - every time you are updating "next", you're setting that to be yet another new node. Each node has their own "next" that leads to the next node, so you aren't going to lose anything by doing it this way. I didn't actually test your code but since "Start" points to the first node always, you aren't going to lose any nodes along the way. A debugger should help if you're curious to learn more about how this works!
I was trying to reverse a linked list, however whenever I execute the following function, I get only the last element. For example, if the list contained 11,12,13 earlier. After executing the function, it contains only 13. Kindly point out the bug in my code
void reverselist() {
struct node *a, *b, *c;
a = NULL;
b = c = start;
while (c != NULL) {
c = b->next;
b->next = a;
a = b;
b = c;
}
start = c;
}
Doesn't your loop guard insure that start is null?
If you aren't using start to identify the first element of the list, then the variable you ARE using is still pointing to what WAS the first element, which is now the last.
c is a helper pointer.
void reverselist()
{
struct node *a, *b, *c;
a=NULL;
b=start;
while(b!=NULL)
{
c=b->next
b->next=a;
a=b
b=c
}
start=a;
}
// You should assume that Node has a Node* called next that
// points to the next item in a list
// Returns the head of the reversed list if successful, else NULL / 0
Node *reverse( Node *head )
{
Node *prev = NULL;
while( head != NULL )
{
// Save next since we will destroy it
Node *next = head->next;
// next and previous are now reversed
head->next = prev;
// Advance through the list
prev = head;
head = next;
}
return previous;
}
I would have made a prepend function, and done the following:
struct node* prepend(struct node* root, int value)
{
struct node* new_root = malloc(sizeof(struct node));
new_root->next = root;
return new_root;
}
struct node* reverselist(struct node* inlist)
{
struct node* outlist = NULL;
while(inlist != NULL) {
struct node* new_root = prepend(outlist, inlist->value);
outlist = new_root;
inlist = inlist->next;
}
return outlist;
}
Have not tested this, but guess you grasp the idea of it. Might be just your variable names, which don't describe anything, but I think this approach is cleaner, and easier to understand what actually happens.
EDIT:
Got a question why I don't do it inplace, so I'll answer it here:
Can you do it inplace? Are you sure you don't wish to keep the
original list?
Do you need to do it inplace? Is the malloc to time consuming/is this a performance critical part of your code? Remember: premature optimization is the root of all evil.
Thing is, this is a first implementation. It should work, and not be optimized. It should also have a test written before this implementation is even thought of, and you should keep this slow, un-optimized implementation until the test passes, and you have proved that it's to slow for your use!
When you have a passing unit test, and proven the implementation to be to slow, you should optimize the code, and make sure it still passes the test, without changing the test.
Also, is it necessary inplace operations which is the answer? What about allocating the memory before reverting it, this way you only have one allocation call, and should hopefully get a nice performance boost.
This way everyone is happy, you have a cleaner code and avoid the risk of having Uncle Bob showing up at your door with a shotgun.