Does return; do anything in this function? - c

I'm trying to learn linked list following the instructions given in this tutorial and I can't seem to understand whether if the return; in the if statement at step 4 of the following code does anything...
/* Given a reference (pointer to pointer) to the head
of a list and an int, appends a new node at the end */
void append(struct Node** head_ref, int new_data)
{
/* 1. allocate node */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
/* 2. put in the data */
new_node->data = new_data;
/* 3. This new node is going to be the last node, so make next of it as NULL*/
new_node->next = NULL;
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL)
{
*head_ref = new_node;
return; //<<<this return here
}
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
return;
}
would the following be equally functional as the above if there was no return;statement like this?
/*4. If the Linked List is empty, then make the new node as head */
if(*head_ref == NULL)
{
*head_ref = new_node;
}
/*step 5*/
/*step 6*/

It does the same thing it does anywhere else, it exits from the function immediately.
If you don't return, you'll continue executing the function, but it won't work properly. The next block of code is:
while (last->next != NULL)
If that if was true, then last == NULL (because of the initialization last = *head). This code will try to indirect through the null pointer, which is undefined behavior.
There's no point in executing the rest of the code, which appends the new node after the last node in the list. The list was empty, so there's no last node to append to. You've already inserted it as the first node in the if block.
BTW, if is not a loop. A loop executes code repeatedly, and they're written using for and while. if is a conditional, it either executes the code once or it doesn't execute it at all.

Yes it changes the code flow. When the condition in 4 is true then *head_ref = new_node; is executed and the function then returns with no more processing.
If you take the return out then 5 and 6 will always be executed regardless of whether 4 is or not.
An alternative style (avoiding having a return in the middle of the function) would be to have an else for steps 5 and 6. Like the following:
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL)
{
*head_ref = new_node;
}
else
{
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
}
Note there is no need to have the return at the last line as the function does not return a value.

Related

Segmentation fault with unknown reason (C)

I was trying to solve my data structure homework, and I used doubly linked list in my code. But when I used it to create a new node and return to main, I got a segmentation fault.
my function(append) is like:
void append(struct Node** RailHead[], int new_data, int r){
/* 1. allocate node */
printf("1.\n");
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
struct Node* last = RailHead[r]; /* used in step 5*/
/* 2. put in the data */
new_node->data = new_data;
/* 3. This new node is going to be the last node, so
make next of it as NULL*/
new_node->next = NULL;
/* 4. If the Linked List is empty, then make the new
node as head */
if (RailHead[r] == NULL){
new_node-> prev = NULL;
RailHead[r] = new_node;
return;
}
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
/* 7. Make last node as previous of new node */
new_node-> prev = last;
return;
}
RailHead is the pointer array that I adopted to store the different head reference of doubly linked lists, and r is the assigned rail (by input) to decide which rail should append new data.
I would like to understand what's going on with my code. If anyone can help, I will be really appreciate about it! Thank you!

What is the Significance of return in this if condition & while statement?

/* Given a reference (pointer to pointer) to the head
of a list and an int, appends a new node at the end */
void append(struct Node** head_ref, int new_data)
{
/* 1. allocate node */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
/* 2. put in the data */
new_node->data = new_data;
/* 3. This new node is going to be the last node, so make next
of it as NULL*/
new_node->next = NULL;
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL)
{
*head_ref = new_node;
return;
}
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
return;
}
What is the use of the return keyword in the above if & while condition?
Is it necessary to give this return keyword or without this also my program will work fine?
The return keyword is optional for functions with return type void (since they don't return anything). However, if the return statement is used, it immediately stops the program flow of the function and returns from the function. In your example, using the return within the if statement ensures that the function will not further execute if the condition is true.
However, note that return statement is required if the return type of the function is not void.

Why does my code not insert a new node into this linked list?

I am new to programming. I just want to know why this doesn't work.
My understanding of pointers isn't clear, especially when using pointers across functions.
void append(struct Node** head_ref, float new_data)
{
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
new_node->data = new_data;
new_node->next = NULL;
while (last != NULL)
last = last->next;
last = new_node;
return;
}
void append(struct Node** head_ref, float new_data)
{
while (last->next != NULL)
last = last->next;
last->next = new_node;
return;
}
In the first function the new data doesn't get included, I get only the original linked list.
But the second function works just fine. How does a double pointer work when inserting a new node in the beginning of the linked list? (I have seen answers regarding this question, but I am still confused)
In the first example, you move the pointer last until it points at a NULL location. Then, you set the pointer to new_node. However, at this point, last has no real association to your linked list. It is just a pointer to some memory. In the second example, the correct one, you iterate until you reach the tail of the linked list, where next of that node is NULL. Then, you set that next to new_node. There is now a new tail to the list, that is new_node.
Changing the local variable last does not change the value of the data member next of the previous (last) node.
To be more clear let's assume that the list is empty. Then you have to change the pointer referenced by this double pointer head_ref.
You declared a new pointer
struct Node *last = *head_ref;
The loop
while (last != NULL)
last = last->next;
is skipped because now already last is equal to NULL die to the initialization in the previous declaration. And then you changed this local variable last
last = new_node;
The original value of the pointer pointed by head_ref was not changed because last and *head_ref occupy different extents of memory. You changed the memory occupied by last but not changed the memory occupied by head_ref.
Also you should check whether a memory was successfully allocated.
The function can look the following way
int append( struct Node **head_ref, float new_data )
{
struct Node *new_node = malloc( sizeof( struct Node ) );
int success = new_node != NULL;
if ( success )
{
new_node->data = new_data;
new_node->next = NULL;
while ( *head_ref != NULL ) head_ref = &( *head_ref )->next;
*head_ref = new_node;
}
return success;
}
As for this loop (I think you wanted just to show the loop not a whole function)
while (last->next != NULL)
last = last->next;
last->next = new_node;
then you are changing the data member next of the previous (last ) node.
Though this loop will not work if initially head_ref is equal to NULL.

Linked list traversing with and without the next field

I have written two functions while learning Linked list. First counts and returns number of nodes in the list. Second adds new node towards the end of the list. Can you please help me understand why I need to use "current->next" to check for NULL in my code below in the addatend function? I did not have to use it for first function. Without that my exe crashes with bad pointer...
Thank you very much for your time and help.
int length(node * head) {
node * current = head;
int count = 0;
while(current != NULL){ // This line works as expected....
count ++;
current = current->next;
}
return count;
}
void addatend (node * head, int value){
node * newnode = (struct node *) malloc(sizeof(struct node));
node * current = head;
while (current != NULL){ // This line would not work?? If I use current->next != NULL it works.....
current = current->next;
}
current->next = newnode;
newnode->data = value;
newnode->next = NULL;
}
In the second function your are accessing current->MEMBER after you changed it to current->next, while in the first one you are not. That is, in the first one you just run to end, and end up with a NULL pointer, but you don't use it, so it's ok. In the second one, you do use it.
In order not to mess up with your original linked-list you copy the head to the current, and then you can do what you want with that current node.
Linked lists are generated like:
typedef struct e
{
int data;
struct e *next;
} node;
which means each node (in my example) has a data of type integer and a pointer to the next node. Let's suppose that our list has 4 objects:
1->2->3->4->NULL
the first node (data=1) point to its next node (data=2) and so on till last node (data=4) which the next node does not exist so will point to NULL.
in the function "addatend" in order to add new node to the end of the linked-list you should travel through the last element of the list (data=4 in my example), which the node next to it will point to NULL, so you should go through the list till the end and when you arrive there, you can add the "newnode".

Printing contents of linked list only prints first node

This is the updated version of the code. i am trying to add some info in a link list every time a client sends a message to the server (it can be multiple clients). Every time a new message arrives, the function checks its time so it could add it to the list before or after the previous node depending on its current time. The server must print the messages in order of arrival. If there are equal time stamps then it should move to sort the equal ones in using the ids of the servers.
Here is my struct for the list:
'typedef struct trade_list {
char* trader_msg;
u_int32_t id_of_sender;
int sender_timer;
int local_time;
struct trade_list *next;
}trade_list;
trade_list *head = NULL;
the following is the function that inserts to list and sorts according to time:
void add_transaction (char* received_msg_IN, int curr_time_IN, u_int32_t my_id_IN, int elapsedIn)
{
/* Find the node with the smallest time >= curr_time_IN. 'found' starts as NULL, then
is always the node before 'cur'. 'cur' moves through the list until its time is
less than 'curr_time_IN'. So at the end, 'cur' is the first node that's too
far in, and 'found' is either NULL or the node we insert after. */
trade_list *newnode, *cur, *found;
found = NULL;
for (cur = head; cur && cur->sender_timer <= curr_time_IN; cur = cur->next)
found = cur;
if (found) {
/* If 'found' isn't NULL, we're inserting after it*/
/* Times match: Sort out in another way*/
} else {
newnode = malloc(sizeof(*newnode));
newnode->trader_msg = malloc(strlen(received_msg_IN)*sizeof(received_msg_IN));
strcpy(newnode->trader_msg,received_msg_IN);
newnode->sender_timer = curr_time_IN;
newnode->id_of_sender = my_id_IN;
newnode->local_time = elapsedIn;
newnode->next = found->next;
found->next = newnode;
}
} else {
/* No node with more recent time found -- inserting at 'head' */
newnode = malloc(sizeof(*newnode));
newnode->trader_msg = malloc(strlen(received_msg_IN)*sizeof(received_msg_IN));
strcpy(newnode->trader_msg,received_msg_IN);
newnode->sender_timer = curr_time_IN;
newnode->id_of_sender = my_id_IN;
newnode->local_time = elapsedIn;
newnode->next = head;
head = newnode;
}
EDITED AFTER NEW PROBLEM
I managed to sort the list using a sorting method instead later on. So now my list gets sorted just fine. It prints just fine swell, the new problem that arose now is that I want to delete the current node after I print it. So after it gets print it gets deleted. I used the following function but my app crashes.
void deletefirst (struct trade_list *head) {
struct trade_list *tmp = *head;
if (tmp == NULL) return;
*head = tmp->next;
free (tmp);
}
i call this function from my print function:
void print_trades()
{
trade_list * newnode = head;
while (newnode) {
if ((elapsed - newnode->local_time >= 8))
{
printf ("%s\n", newnode->trader_msg);
newnode = newnode->next;
deletefirst(newnode);
}
}
}
How would I delete the current node and move on?
The way you print the list should work fine, as long as there is at least one node. I'd recommend changing the do { ... } while() to just a while() { ... } though, so that it still works when the list is empty and head is NULL:
trade_list *currentnode = head;
while (currentnode) {
printf ("Trade: %s Time: %d ID: %d\n",
currentnode->trader_msg,
currentnode->sender_timer,
currentnode->id_of_sender);
currentnode = currentnode->next;
}
There are some problems with the way you add to the list, though. When head is not null, you're removing its link to the rest of your list:
if (head == NULL)
{
... /* skipped for brevity */
}
else
{
currentnode->next = NULL;
head = currentnode;
}
Since at this point currentnode == head, you're pointing head->next to NULL (if there were another node, you'd be throwing it away) and then assigning head to itself.
Your insertion code in general doesn't look right. If you want to insert only at the beginning of the list, you just need something like:
trade_list *newnode = malloc(sizeof(trade_list));
/* *** Fill out newnode's fields here *** */
newnode->next = head;
head = newnode;
If you want to insert after an arbitrary node, you have to find it first by walking the list, then do something like this (keeping later times at the head of the list):
trade_list *newnode, *cur, *found;
/* Find the node with the smallest time >= curr_time_IN. 'found' starts as NULL, then
is always the node before 'cur'. 'cur' moves through the list until its time is
less than 'curr_time_IN'. So at the end, 'cur' is the first node that's too far in,
and 'found' is either NULL or the node we insert after. */
found = NULL;
for (cur = head; cur && cur->sender_timer >= curr_time_IN; cur = cur->next)
found = cur;
if (found) {
/* If 'found' isn't NULL, we're inserting after it (or skipping if the times match,
since that seems to be what the original code was trying to do) */
if (found->sender_timer == curr_time_IN) {
/* Times match: skip it */
printf("SKIPPED\n");
} else {
/* inserting after 'found' */
newnode = malloc(sizeof(*newnode));
/* *** Fill out newnode's fields here *** */
newnode->next = found->next;
found->next = newnode;
}
} else {
/* No node with more recent time found -- inserting at 'head' */
newnode = malloc(sizeof(*newnode));
/* *** Fill out newnode's fields here *** */
newnode->next = head;
head = newnode;
}
Edited after comment:
To change the list to sort descending by time, then ascending by ID when the times match, just a couple of changes are needed.
Edit
Since the original for loop to find the node continues to the last node that meets the criteria, we just have to add a test into the loop to break when we're at the right one... that is, to break if the next node has equal time and a higher ID, since in that case we want to insert before it. (the earlier edit was flawed... sorry about that).
Additionally, the if (found->sender_timer == curr_time_IN) check afterwards is no longer needed, since there's no skipping and the sort by ID was handled by the new for loop.
So that section of code becomes:
/* original search */
for (cur = head; cur && cur->sender_timer >= curr_time_IN; cur = cur->next) {
/* *** added condition for ID sort */
if (cur->sender_timer == curr_time_IN && cur->id_of_sender >= my_id_IN) break;
found = cur;
}
if (found) {
/* If 'found' isn't NULL, we're inserting after it */
/* CHANGED: no need to skip when times are equal */
newnode = malloc(sizeof(*newnode));
/* *** Fill out newnode's fields here *** */
newnode->next = found->next;
found->next = newnode;
} else {
/* No node with more recent time found -- inserting at 'head' */
newnode = malloc(sizeof(*newnode));
/* *** Fill out newnode's fields here *** */
newnode->next = head;
head = newnode;
}
The problem is not with the printing code it is in the addition.
It looks like when adding a new node you allocate the node but does not connect it to the head or any of the other nodes.
You must have a code like this to add to the beginning of the list:
new_node->next = head;
head= new_node;
Also, the logic you used is obscured and you have many code duplications.
Adding to a linked list has only two options: beginning of the list (head) or on any of the other nodes, you do not need all this logic.

Resources