how to remove code duplications in this code? - c

I am a new C99 programmer and want the help of the community on this one.
I wrote the following function which receives two pointers for a node (Or simply Node) and a pointer to a pointer to node (Or *Node) an merges them together into one sorted Node.
This is Given:
typedef struct node_t {
int x;
struct node_t *next;
} *Node;
typedef enum {
SUCCESS = 0,MEMORY_ERROR, EMPTY_LIST, UNSORTED_LIST, NULL_ARGUMENT,
} ErrorCode;
int getListLength(Node list);
bool isListSorted(Node list);
This is the code I wrote:
ErrorCode mergeSortedLists(Node list1, Node list2, Node *merged_out)
{
if (merged_out==NULL)
return NULL_ARGUMENT;
if (list1==NULL || list2==NULL)
return EMPTY_LIST;
if (!isListSorted(list1) || !isListSorted(list2))
return UNSORTED_LIST;
Node ptr=*merged_out;
int list1_len=getListLength(list1),list2_len=getListLength(list2);
for (int i=0;i<list1_len+list2_len;i++)
{
int min=0;
if (list1!=NULL && (list2==NULL || (list2!=NULL && list1->x<=list2->x))){
min = list1->x;
list1=list1->next;
}
else{
min=list2->x;
list2=list2->next;
}
ptr->x=min;
if (i==list1_len+list2_len-1){
ptr->next=NULL;//The next for the last Node should be Null
continue;
}
ptr->next=malloc(sizeof(*ptr));
if (ptr->next==NULL){
//We should Free all previosly allocated memory
//except for the first node since it was not allocated via malloc
return MEMORY_ERROR;
}
ptr=ptr->next;
}
ptr=NULL;
return SUCCESS;
}
But after reviewing my code I was told it has a lot of code duplications (more precisely inside he for loop) which should be corrected by using external functions, do u notice any code duplications? and how to fix that, any ideas?

Related

Getting a segmentation fault when trying to insert into a sorted linked listed

I'm just trying to insert random integers into a linked list in a sorted manner.
node_type *head=NULL;
for(int i=1;i<=10;i++)
{
input=rand()%101;
insert(input,&head);
}
Function definition for insert():
void insert(int input,node_type ** list)
{
node_type * temp=malloc( sizeof(node_type));
if(temp==NULL)
printf("Error,memory allocation unavailable");
(*temp).num=input; // filling up with the user's input
node_type *tempHead=*list;
node_type *prev=NULL;
if(tempHead==NULL) // if list is empty, I simply insert the node at start of list
{
(*temp).link=*list;//inserting it at the start of the list
*list=temp;
return;
}
while( (tempHead->num)<=input && tempHead!=NULL)
{
prev=tempHead;
tempHead=(*tempHead).link;
}
prev->link=temp; //Preceding node of new node now points to new node
temp->link=tempHead; //new node points to succeeding node
}
It seems that the segfault happens after looping through this section of code once
while( (tempHead->num)<=input && tempHead!=NULL)
{
prev=tempHead;
tempHead=(*tempHead).link;
}
Edit:
Definition of node_type:
typedef struct node_type node_type;
struct node_type
{
int num;
node_type *link;
};
Seems like a stupid question but I couldn't seem to figure out what went wrong in this section of code
There are several subtle problems with your code.
The one mentioned in the comment. In the while loop, you dereferenced tempHead before testing it for NULL value.
Take the following expression as example: ( tempHead->num<=input && tempHead!=NULL) The logical operators evaluates expression in-order and short-circuits. This means tempHead->num<=input is evaluated first, and if tempHead is NULL, it SegFaults.
You didn't properly handle prev being NULL right after the while loop.
This is because you simultaneously tested for tempHead being non-NULL and node value satisfying the list order. Therefore, even if tempHead is non-NULL, you could still miss the chance to assign to prev.
After you've traversed to the point where you can instert the temp node into the list, you should test for prev before dereferencing in the assignment prev->link=temp;. At this point, you need to handle 2 cases:
you're in the middle of the list, and
you're at the beginning of the list and prev is NULL.
I'll just post a whole working example and let you compare the difference. This is a hard practice, and even the most professional C programmers can miss these types of flaws
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct node_type node_type;
struct node_type {
int num;
node_type *link;
};
node_type *head=NULL;
void dump()
{
for(node_type *n=head; n; n=n->link)
printf("%d ", n->num);
printf("\n");
}
void insert(int input,node_type ** list);
int main()
{
for(int i=1;i<=10;i++)
{
int input=rand()%101;
printf("i=%d, input=%d\n", i, input);
insert(input,&head);
}
dump();
}
void insert(int input,node_type ** list)
{
node_type * temp=malloc( sizeof(node_type));
if(temp==NULL)
printf("Error,memory allocation unavailable");
memset(temp, 0, sizeof(node_type)); // [Problem 0]: Initialize the structure.
temp->num=input; // filling up with the user's input
node_type *tempHead=*list;
node_type *prev=NULL;
if(tempHead==NULL) // if list is empty, I simply insert the node at start of list
{
//temp->link=*list; // [Problem 1]: This statement caused a loop list.
*list=temp;//inserting it at the start of the list
return;
}
//while( (tempHead->num)<=input && tempHead!=NULL) // [Problem 2]: wrong condition expression
while( tempHead!=NULL && tempHead->num <= input ) // this is the correct expression order.
{
prev=tempHead;
tempHead=tempHead->link;
}
if( !prev ) // tempHead != NULL but tempHead->num < input.
{
temp->link = *list;
*list = temp;
}
else // we're in the middle of the list.
{
printf("%p, %p, %p, \n", prev, temp, tempHead);
prev->link=temp; //Preceding node of new node now points to new node
temp->link=tempHead; //new node points to succeeding node
}
}

Double free error with doubly linked list

So I'm trying to do a method to clear a doubly linked list for school where the doubly linked list and nodes are defined as:
struct word_entry
{
char *unique_word ;
int word_count ;
} ;
struct node
{
struct word_entry one_word ;
struct node *p_previous ;
struct node *p_next ;
} ;
struct linked_list
{
struct node *p_head ;
struct node *p_tail ;
struct node *p_current ;
} ;
I have a method to clear a linked list by doing
int clear_linked_list( struct linked_list *p_list ) //return how many nodes were cleared
{
if (p_list->p_head == NULL) {
return 0;
}
else {
int count = 0;
struct node *curr = p_list->p_head;
while (curr != NULL) {
struct node *next = curr->p_next;
free(curr->one_word.unique_word);
free(curr);
curr = next;
count++;
}
return count;
}
}
I do a free() on curr->one_word.unique_word because it's a malloc'd char array. I was taught to free when I use malloc, so that's there.
The issue I run into is I get a "bogus pointer (double free?)" and a core dump when I run the test file provided by my professor. I've worked on this for a few hours and can't seem to find out where (or how) I'm calling free twice.
When you loop through the list, you should constantly change the position of the head, so that even if you repeat clear_linked_list, you will not get an error.
int clear_linked_list(struct linked_list* p_list) // return how many nodes were cleared
{
if (p_list->p_head == NULL) {
return 0;
} else {
int count = 0;
while (p_list->p_head != NULL) {
struct node* curr = p_list->p_head;
p_list->p_head = p_list->p_head->p_next;
free(curr->one_word.unique_word);
free(curr);
count++;
}
return count;
}
}
When freeing memory it is a good practice to set NULL to pointers that were freed to avoid this kind of problems.
So you should do:
free(curr->one_word.unique_word);
curr->one_word.unique_word=NULL;
//if that one_word.unique_word was shared between multiples nodes that free could cause problems if you dont set it to NULL afterwards
free(curr);
curr=NULL; //or curr=next...
Also. Check that when you create the nodes that:
*p_next is NULL on the last node of the double linked list
*p_previous is NULL on the first node of the list
You don't null out p_head before you leave the clear function.
So, if you called it twice, you'd have problems (i.e. p_head would point to an already freed node). Likewise for p_tail.
Also, if you tried to add to the list again, you'd have similar problems.
Otherwise, your clear code is just fine.
So, can you prove that the list is constructed correctly (e.g. before you free, add a printf that prints out all the node's pointers before you free anything).

How to implement a "contains" function for linked lists in C

I'm currently doing an assignment for uni and I need to find the sum of a graph.
To do this I believe I need a linked list that I can use to remember which nodes have been visited. I have the linkedlist working correctly but I can't get a contains function to work. This is the code I have:
struct listnode
{
struct N *val;
struct listnode *next;
};
int contains(struct listnode *head,struct N* value)
{
struct listnode *current = head;
while (current)
{
if ((current -> val) == value)
{
return 1;
}
current = current -> next;
}
return 0;
}
note: N is a node of the graph.
Can anyone see any problems with what I'm doing?
EDIT: contains function should return 1 when N *value is in the list, 0 otherwise
EDIT2:
I have a push function:
void push(struct listnode *head,struct N *value)
{
if (head)
{
struct listnode *current = head;
while (current->next)
{
current = current -> next;
}
current->next = malloc(sizeof(struct listnode*));
current->next->val = value;
current->next->next = NULL;
}
else
{
head = malloc(sizeof(struct listnode*));
if (head)
{
head -> val = value;
head -> next = NULL;
}
else
{
printf("error");
exit(0);
}
}
}
and I want the following line to return 1:
contains(push(visited,p),p);
where p is a pointer to a struct N and visited is my global linked list
EDIT3:
this is my final sum function that I believe should work, but doesnt because of contains.
long sum(struct N *p)
{
if (p)
{
if (contains(visited,p) == 0) //if p hasnt been visited
{
push(visited,p); //make it visited
return (p -> data) + sum(p -> x) + sum(p -> y) + sum(p -> z);
}
else
{
return 0;
}
}
else
{
return 0;
}
}
Your contains function appears to be fine. The issue is that you are always passing a NULL list to it, which is caused by a faulty push function. You need a return in push, or to pass in a pointer with one more level of indirection, so you can assign to head outside of push. One more possible improvement is to notice that no matter what you pass in, the malloc and initialization of a new node is actually the same.
Finally, the main issue, that is really the most likely to cause a segfault is the fact that you are allocating enough space for a pointer to a node, not for the node itself.
Here is an example:
#ifdef BY_INDIRECTION
#define RET_TYPE void
#define IN_TYPE struct listnode **
#else
#define RET_TYPE struct listnode *
#define IN_TYPE struct listnode *
#endif
RET_TYPE push(IN_TYPE head, struct N *value)
{
struct listnode *current, **next;
if(head)
{
for(current = head; current->next; current = current->next) ;
next = &(current->next);
}
else
{
#ifdef BY_INDIRECTION
next = head;
#else
next = &head;
#endif
}
*next = malloc(sizeof(struct listnode));
if(!*next) {
printf("error");
exit(0);
}
(*next)->val = value;
(*next)->next = NULL;
#ifndef BY_INDIRECTION
return head
#endif
}
I have included both suggestions here. If you want to read the one where we use indirection (pass in a listnode ** and have void return), choose the path where BY_INDIRECTION is defined. If you want to have head returned (and pass in just a regular listnode *) read the path where BY_INDIRECTION is not defined.
The latter approach has a return value, so it can be used to write a shortened form like if(contains(push(head, value), value)) { ... }. The former approach does not, so you would have to do
push(&head, value);
if(contains(head, value)) { ... }
I would recommend using the indirect approach regardless because there are very few instances that you would want to check for containment after putting in a value.
This comparison:
if ((current -> val) == value)
it's comparing pointers. If you call your contains() function this way...
...
struct N val_to_find;
...
result = contains (list, &val_to_find);
You will never find the value, even if the contents of val_to_find are the same as the contents of any struct whose pointer is stored in the list.
If your intention for contains() is to find nodes that have the same data, and not just the same pointers, I'd suggest you something like this:
if (struct_n_comparing_function (current -> val, value) == EQUAL) ...
Where struct_n_comparing_function should have the following prototype:
int struct_n_comparing_function (struct N *a, struct N *b);
which compares the contents of the two structs pointed by a and b and return EQUAL if all the fields of the struct pointed by a have the same value as the fields of struct pointed by b.

deleting a node in linear single linked list given the start of the list

I have the following code which deletes a given node from the linear single linked list.
I want to know if we can still improve this program and does it break anytime
struct node
{
int num;
struct node *next;
} ;
typedef struct node s;
void delete(struct node *first)
{
int flag = 0;
s *ptr, *lastNodePtr = NULL, *deleteNode;
deleteNode = (s*) malloc(sizeof(s));
printf("enter the node value to delete");
scanf_s("%d",&deleteNode->num);
deleteNode->next = NULL;
for (ptr=first;ptr!=NULL;ptr=ptr->next) //at least one element exist
{
if(deleteNode->num == ptr->num)
{
flag=1;
if(ptr==first) //need to delete at first place
{
free(ptr);
first = null; //i dont think we need to do this as it points to ptr and ptr is already null.
}
else // need to delete some where middle.it can be last as well.
{
lastNodePtr->next=ptr->next;
free(ptr);
}
printf("successfully deleted..");
break;
}
lastNodePtr=ptr; // taking note of last node visited..
}
if (flag==0)
{
printf("\n Couldn't find the node");
return;
}
}
if ptr is the first element in the list to delete, you set first to null, not to the next of ptr. (sideeffect: you are not able to free the the rest of the list)
your EDITH: delete should return the new Head, better make it a struct node **first parameter which changes the first element if the first is the deleted one
BTW: never cast the result of malloc.
BTW two. why use for-loop? everybody uses while-loop with linked lists
BTW three: normal variable names for linked lists are "head", "list", "next", "prev", "last" with the nice side-affect, they are all the same length, so making it neatly aligned.
struct node
{
struct node *next;
int num;
} ;
void delete(struct node **pp, int num) {
struct node *del;
for ( ;*pp; pp= &(*pp)->next) {
if((*pp)->num == num) break;
}
if (!*pp) { printf("Couldn't find the node(%d)\n", num); return; }
del = *pp;
*pp = del->next;
free(del);
}
BTW: there is nothing wrong with for() loops; they allow you to put all the loop logic on one line.

Trying to delete an element from a double linked list in c

Ι have a problem while trying to delete an element from a double linked list in c.
Nodes_t *remove_Nodes (Nodes_t *a, Nodes_t b){
Nodes_t *head;
head=a;
if ((a->i_pos==b.i_pos) && (a->j_pos==b.j_pos)){
if (a->next=NULL){
return NULL;
}
else {
head=a->next;
head->previous=NULL;
return head;
}
}
else if ((a->i_pos!=b.i_pos) || (a->j_pos!=b.j_pos)){
while ((a->next->i_pos!=b.i_pos)||(a->next->j_pos!=b.j_pos)){
a=a->next;
if (a->next=NULL){
return head;
}
}
a=a->next;
a->previous->next=a->next;
if (a->next=NULL){
return head;
}
else if (a->next!=NULL){
a->next->previous=a->previous;
return head;
}
}
return head;
}
It gets a double linked list, finds an element of type Nodes_t and then deletes it.
Although, as I have checked the list and its pointers work fine, when I try to call the function to delete my first element I get a seg fault.
More specifically, as I have checked, the function proceeds just fine until it gets to this point
else {
head=a->next;
head->previous=NULL;// HERE
return head;
}
The struct I use is this
typedef struct Nodes {
char position;
int i_pos, j_pos;
int g_distance;
int h_distance;
int F_estim;
struct Nodes *parent;
struct Nodes *next;
struct Nodes *previous;
}Nodes_t;
You have used an assignment = instead of a comparison == here:
if (a->next=NULL){
Which will evaluate to NULL, which is false, so will go to your else clause, where you do
head=a->next;
head->previous=NULL;
So head becomes NULL, then you are trying to dereference a NULL pointer to get its previous member.
Quick fix: Add the missing = to the first line I quoted.
Better fix: Refactor your code. It is too long and has unnecessary bits. And don't forget to check your equals operations.

Resources