C - passing sub linked list to functions - c

I'm working in this simple program with list but I'm having a bad time passing pointers.
Let's say I have a struct for students
typedef struct student{
char lastname[50];
int age;
int std_id;
struct student * next;
struct student * prev;
}stdn;
and I have another struct for classes
typedef struct class{
char class_id[3];
struct class * next;
struct class * prev;
struct student * stdn_list;
}clss;
so basically I have this list with classes, and each class contain a sublist with students.
so, here is the function creating the class list, and it works!
void create_class_list(clss ** root, clss * node){
clss * root_aux;
if(!(*root)){
(*root) = node;
}
else{
root_aux = (*root);
while(root_aux->next != NULL){
root_aux = root_aux->next;
}
node->prev = root_aux;
root_aux->next = node;
}
}
my problem is when I need to work with the sublist in each node of the class list.
Here is the function in charge of creating the sublist, and it works
void assign_student(clss ** root, stdn * node, char * class_id){
clss * root_aux;
stdn * stdn_aux;
root_aux = (*root);
while(root_aux != NULL){
if(strcmp(root_aux->class_id,class_id) == 0)
break;
root_aux = root_aux->next;
}
if(root_aux != NULL){
if(root_aux->stdn_list == NULL){
root_aux->stdn_list = node;
}
else{
stdn_aux = root_aux->stdn_list;
while(stdn_aux->next != NULL){
stdn_aux = stdn_aux->next;
}
node->prev = stdn_aux;
stdn_aux->next = node;
}
}
}
Basically this function looks for the specific class and add the student to that class.
My problem is when I want to delete a student, or sort the list using an algorithm like bubblesort, here's an example of a function to delete a student.
void delete_student(clss ** root, int stdn_id){
clss * root_aux;
stdn * stdn_aux;
stdn * temp;
int deleted=0;
root_aux = (*root);
while(root_aux != NULL){
stdn_aux = root_aux->stdn_list;
//try with root first//
if(stdn_aux->std_id == stdn_id){
temp = stdn_aux;
stdn_aux = stdn_aux->next;
stdn_aux->prev = NULL;
free(temp);
deleted = 1;
}
//if the student isn't the root
if(deleted == 0){
stdn_aux = stdn_aux->next;
while(stdn_aux != NULL){
if(stdn_aux->std_id == stdn_id){
temp = stdn_aux;
//link the prev element with the next element
stdn_aux->prev->next = stdn_aux->next;
//link the next element with the prev element
stdn_aux->next->prev = stdn_aux->prev;
stdn_aux = stdn_aux->next;
free(temp);
deleted = 1;
break;
}
stdn_aux = stdn_aux->next;
}
}
if(deleted == 1){
break;
}
root_aux = root_aux->next;
}
}
The function just looks like don't delete the element from the list and I'm not sure if is something with how I pass the pointer to the function, or how I create the list in first place.

When you delete a student node that is at the head of the Student List, you need to re-assign to root_aux->stdn_list as you are deleting the node it currently points to. That must be why it appers you are not deleting a student node.
root_aux->stdn_list = stdn_aux->next;
There are other issues with regard to processing that should be wrapped in if statements to prevent the program from core dumping:
Before you begin processing the student list, you need to first check that there is a student list. That is, check that the class variable pointing to the student list - root_aux->stdn_list - is not NULL.
Before doing the following statements, make sure that stdn_aux->next is not NULL, that is, there is stuff beyond the root node you are deleting.
stdn_aux = stdn_aux->next;
stdn_aux->prev = NULL;
Before making this assignment
stdn_aux->next->prev = stdn_aux->prev;
check that stdn_aux->next is not null because this is the last node in the student list.

Related

Loop through a node list

I'm fairly new to coding in c and have stumbled upon a problem. The code below is what I have so far and the problem at hand is how to looping through the node job_list.
We have two nodes, the first one creates a basic job post with a reference number (an integer) and job name (a string - an array of characters) as parameters, and the second is a list that holds job posts with the job_node being one parameter and then the second one being a standard struct job_list * next parameter.
I think you mixed few things up..
singly link list (that's what i understand you are up to) is not a fixed size data structure. it even not an increasing space data structure. link list use exactly the amount of memory you want to hold in it.
link list is a list of nodes. you can create list - and it will be an empty list.
then, you can add nodes with your desired data and every node you insert - create a new node (on the heap = dynamically allocated) which will contain your data and will be linked to the list.
every data node in the list will have a node_next it will point to, and node_prev which will point at this particular node(head and tail will not have this 2 nodes. head will point at node_next but will not have node_prev which point to it, and tail only have node_prev which will point to it).
so, if you want to create list you will have to dynamically allocate the space of the list which contain 2 nodes: head and tail(or end).
this is an example of create function:
slist_t *SlistCreate(void)
{
slist_t *list = malloc(sizeof(slist_t));
slist_iter_t dummy = NULL;
if(NULL == list)
{
return (NULL);
}
dummy = malloc(sizeof(node_t));
if(NULL == dummy)
{
free(list);
return (NULL);
}
dummy->next = NULL;
dummy->data = (void *)list;
list->head = dummy;
list->tail = dummy;
return (list);
}
then you will be able to insert nodes before particular node or after - that's depend on how you will implement this:
if you want to implement insert before:
you will have to encapsulate the list struct and prevent the user from sending your head to insert before.
or you can check every time if your insert function get the head of the list and update the head of the list (you can insert after and copy the head data to the head->next node and then use the head for the data the user wanted to be at the head of the list.
if you want to implement insert after:
you will have to check if the user sent you the tail (also called dummy_node cause it point to dummy_node->next = NULL), and use the same method i mentioned before, only the opposite way..
this is an example of insert before - in this example i used slist_iter_t which is pointer to node_t - the user isn't exposed to any of this structs (not to the list struct or the nodes struct):
slist_iter_t SlistInsert(slist_iter_t iterator, const void *data)
{
slist_t *ptr = NULL;
slist_iter_t dup = (slist_iter_t)malloc(sizeof(node_t));
if(NULL == dup)
{
return(NULL);
}
assert(NULL != iterator);
dup->data = iterator->data;
dup->next = iterator->next;
iterator->data = (void *)data;
iterator->next = dup;
if(NULL == dup->next)
{
ptr = (slist_t *)dup->data;
ptr->tail = dup;
}
return (iterator);
}
so, for using this DS you will have to write create function, insert function and destroy function (you will have to free all your dynamically allocated memory).
you may add more function such as remove, search data, clear list, is empty and so on. if you choose to encapsulate this implementation and hide from the user the struct of the list and the struct of the node, you will have to add more function such as get data function, get next node, and more..
you mentioned you need to insert if this data doesn't exist in the list so you can send from insert function to find function.
your function needs to look something like this:
struct job_list {
struct job_node * front;
struct job_list * next;
};
struct job_node {
int reference_number;
char * job_name;
struct job_node *next;
};
for your first function:
struct job_node *JobListCreate(void)
{
struct job_node *list = malloc(sizeof(struct job_node));
struct node_job dummy = NULL;
if(NULL == list)
{
return (NULL);
}
dummy = malloc(sizeof(node_t));
if(NULL == dummy)
{
free(list);
return (NULL);
}
dummy->next = NULL;
dummy->data = (void *)list;
list->head = dummy;
list->tail = dummy;
return (list);
}
for your second function:
void JobListInsertInFront(struct job_node *list, int reference_number, char * job_name)
{
slist_t *ptr = NULL;
struct node_job dup = NULL;
assert(NULL != list);
dup = (struct node_job)malloc(sizeof(node_t));
if(NULL == dup)
{
printf("Allocation failed\n");
return;
}
dup->reference_number = list->head->reference_number;
dup->job_name = list->head->job_name;
dup->next = list->head->next;
list->head->reference_number = reference_number;
list->head->job_name = job_name;
list->head->next = dup;
return;
}
and for the last function:
bool JobListInsertIfNotExist(struct job_node *list, int reference_number, char * job_name)
{
slist_t *ptr = NULL;
struct node_job dup = NULL;
assert(NULL != list);
while (NULL != dup)
{
if (dup->reference_number == reference_number && dup->job_name == job_name)
{
return false;
}
dup = dup->next;
}
dup = (struct node_job)malloc(sizeof(node_t));
if(NULL == dup)
{
printf("Allocation failed\n");
return;
}
dup->reference_number = list->head->reference_number;
dup->job_name = list->head->job_name;
dup->next = list->head->next;
list->head->reference_number = reference_number;
list->head->job_name = job_name;
list->head->next = dup;
return true;
}
As Jack Lilhammers pointed out in the comments, your code is very complex and there are a lot of mistakes in it, so I wrote down some general functions that you can then modify accordingly.
This is the basic struct, that we are going to work with:
struct node {
int data;
struct node *next;
};
Create a new node
Then this is how you'd create a new node:
#include <stdio.h>
#include <stdlib.h>
struct node *new_node(int data, struct node *next)
{
struct node *new = malloc(sizeof *new);
if (!new) {
printf("Error: memory allocation failed");
exit(EXIT_FAILURE);
}
new->data = data;
new->next = next;
return new;
}
You would initially call the function like this:
struct node *head = new_node(5, NULL);
Check if a node exists
Normally you would do something like this to check if a node with specific data exists in the linked list:
#include <stdbool.h>
/* Return whether or not the node exists */
bool node_exists(struct node *head, int data)
{
struct node *cursor = head;
while (cursor != NULL) {
if (cursor->data == data)
return true;
cursor = cursor->next;
}
return false;
}
Insert new node at the end
If you want to insert a new node at the end of the linked list, this is how it works:
void insert_last(struct node *head, struct node *new)
{
struct node **cursor = &head;
while ((*cursor) != NULL)
cursor = &(*cursor)->next;
*cursor = new;
}
Insert new node if nonexistent
You can combine the last two functions to only insert a new node at the end of the linked list, if the data doesn't already exist:
#include <stdbool.h>
/*
* Return whether or not the node exists. If it exists,
* insert the new node at the end of the linked list.
*/
bool new_insert_last(struct node *head, struct node *new)
{
struct node **cursor = &head;
while ((*cursor) != NULL) {
if ((*cursor)->data == new->data)
return true;
cursor = &(*cursor)->next;
}
*cursor = new;
return false;
}
This function could be called like this:
new_insert_last(head, new_node(3, NULL));
I have created a GitLab Snippet, so you can see the functions in action.

Why do these code snippets behave differently?

I am relatively new to C, and have been learning about linked lists with pointers.
I learned that
(*foo).bar is the same ad foo->bar.
foo->bar is used because it is more readable.
Therefore I do not understand why these code snippets behave differently:
1)
void appendCourse(CourseNode** pLL, Course c){
CourseNode * root = *pLL;
CourseNode* last = makeCourseNode(c);
if(root != NULL){
CourseNode node = *root;
while(node.pNext != NULL){
node = *node.pNext;
}
node.pNext = last;
} else {
*pLL = last;
}
}
and
2)
void appendCourse(CourseNode** pLL, Course c){
CourseNode * root = *pLL;
CourseNode* last = makeCourseNode(c);
if(root != NULL){
CourseNode *node = root;
while(node->pNext != NULL){
node = node->pNext;
}
node->pNext = last;
} else {
*pLL = last;
}
}
to me it looks like 1) should behave as if dereferencing first, then member access. Sort of like (*foo).bar
but 1) doesn't seem to work right at all, it can only successfully add the first element.
2) does however add all elements into the linked list.
In case this helps: my structs and other method:
typedef struct CourseNode {
struct CourseNode* pNext;
Course course;
} CourseNode;
typedef struct
{
StudentNode *pWaitlistHead; // Waitlist for this course
char szCourseId[12]; // Course Identifier
char szRoom[15]; // Room number of the course
char szDays[15]; // What days the course will meet, ex: MWF, TR, etc
char szTimes[15]; // Meeting Time, ex: 10:00-11:15am
int iAvailSeats; // Number of available seats in the course
double dFee; // Additional fees for the course
} Course;
CourseNode* makeCourseNode(Course c){
CourseNode * node = malloc(sizeof(CourseNode));
node->pNext = NULL;
node->course = c;
return node;
}
CourseNode node = *root;
while(node.pNext != NULL){
node = *node.pNext;
}
This creates a new CourseNode called node. The value of that new CourseNode is modified, but that has no affect on the linked list.
CourseNode *node = root;
while(node->pNext != NULL){
node = node->pNext;
}
Here, node points to a CourseNode that is on the linked list.
The simplest way to understand the difference is that the first code excerpt creates new CourseNodes. It's like the difference between these two:
int foo (int *i)
{
int *j = i; // j is a pointer to the same int i points to
*j = 2; // this changes the value of the int i points to
int j = *i; // this creates a new int
j = 2; // this changes the value of that new int
}

Sorting doubly linked list alphabetical

The task is to sort a list in alphabetical order. That is to be done by changing the pointer variables and not just switching the content of the nodes.
I first wanted to implemend a swap function. that function shall swap 2 nodes. After that I wanted to implement a sorting algorithm. My problem is, that the swaping function does not really work as it should and the algorithm doesnt either (ofc, since the swapping function doesnt even work).
struct student {
char Vorname[51];
char Nachname[51];
int MatNr;
char Adresse[51];
int Kurse;
struct student *next;
struct student *previous;
};
struct student *first = NULL;
struct student *last = NULL;
void swap(struct student *pointer) {
struct student *pointer1, *pointer3, *pointer4;
pointer1 = pointer->previous;
pointer3 = pointer->next;
pointer4 = pointer->next->next;
pointer4->previous = pointer;
pointer->next = pointer4;
pointer1->next = pointer3;
pointer3->previous = pointer1;
pointer->previous = pointer3;
pointer3->next = pointer;
}
This is the not finished sort function. I didnt implement it correctly yet, since the swap function took my attention first.
void sort(void) {
struct student *pointer1, *pointer2, *pointer3, *pointer4;
pointer1 = first->previous;
pointer2 = pointer1->next;
pointer3 = pointer2->next;
pointer4 = pointer3->next;
while(pointer2 != NULL){
if((strcmp(pointer2->Nachname, pointer3->Nachname)) > 0) {
swap(pointer2);
}
pointer1 = pointer1->next;
printList();
}
}
When I run swap(first); the first element doesnt get displayed since the pointer first is now pointing at the second node. Well, thats easily done with first = pointer3;
When I run swap(first->next); there is a similar problem, since it also leaves out one node of the list.
I'm not really sure how to get this function right, since first shouldnt get involved in swapping the 2nd and 3rd node of the list.
I'd appreciate any help that could help me solving this, maybe I'm just overlooking some minor mistake, but I can't really get the solution of this.
Thank you!
Sorting the list by swapping doubly linked nodes seems quite inefficient because you cannot use fast algorithms like merge sort.
You could instead use only the next links in a recursive merge sort function and reconstruct the back links on the resulting list.
Here is how to do it:
struct student {
char Vorname[51];
char Nachname[51];
int MatNr;
char Adresse[51];
int Kurse;
struct student *next;
struct student *previous;
};
struct student *first = NULL;
struct student *last = NULL;
/* Merge two sorted lists. p1 and p2 are != NULL */
struct student *merge(struct student *p1, struct student *p2) {
struct student *head, **pp;
pp = &head;
for (;;) {
if (strcmp(p1->Nachname, p2->Nachname) <= 0) {
*pp = p1;
pp = &p1->next;
p1 = p1->next;
if (p1 == NULL) {
*pp = p2;
break;
}
} else {
*pp = p2;
pp = &p2->next;
p2 = p2->next;
if (p2 == NULL) {
*pp = p1;
break;
}
}
}
return head;
}
/* Recursive top-down merge sort */
struct student *msort(struct student *np) {
struct student *p1, *p2;
/* trivial lists are sorted */
if (np == NULL || np->next == NULL)
return np;
/* locate mid-point using 2 finger method */
for (p1 = np, p2 = np->next; p2 && p2->next; p2 = p2->next->next)
p1 = p1->next;
/* split the list at mid-point */
p2 = p1->next;
p1->next = NULL;
p1 = np;
/* sort the sublists recursively */
p1 = msort(p1);
p2 = msort(p2);
return merge(p1, p2);
}
void sort(void) {
struct student *p1, *p2;
/* sort the list as a singly linked list */
first = msort(first);
/* reconstruct the backlinks */
p1 = NULL;
for (p2 = first; p2; p2 = p2->next) {
p2->last = p1;
p1 = p2;
}
last = p1;
}
As suggested by rcgldr, it may be more efficient to use a bottom-up merge sort to avoid repeated scans of the lists. Here is the alternate code:
/* bottom-up merge sort with sublist array */
struct student *msort(struct student *head) {
struct student *array[32] = { NULL };
int i;
/* handle trivial lists */
if (head == NULL || head->next == NULL)
return head;
i = 0; /* avoid warning */
p1 = head;
/* merge nodes into pending lists of increasing lengths */
while (head != NULL) {
struct student *next = head->next;
head->next = NULL;
for (i = 0; i < 32 && array[i] != NULL; i++) {
head = merge(array[i], head);
array[i] = NULL;
}
/* do not go past end of array */
if (i == 32)
i--;
array[i] = head;
head = next;
}
/* merge pending lists into single list:
* the last element stored into the array is at offset i and
* all entries before it are NULL pointers. */
for (head = array[i++]; i < 32; i++) {
if (array[i] != NULL)
head = merge(array[i], head);
}
return head;
}

Segmentation fault during insertion of node into linked list

I am attempting to alphabetize two separate lists in my add function: one that sorts the nodes by first names, and another that sorts by last names. I also have some logic that checks if a name is already in the list and if it is an error is printed and the list is returned unchanged. Like the title says, I am getting a segmentation fault here and am not sure why. It may be a pretty basic problem but I am new to C and especially new to linked lists.
Here is how the nodes are defined:
typedef struct node {
char *first;
char *last;
long number;
struct node *nextFirst;
struct node *nextLast;
} Node;
typedef struct mlist {
Node *headFirstName;
Node *headLastName;
} MultiLinkedList;
And here is my add function:
MultiLinkedList *add(MultiLinkedList *list, char *first, char *last, long num) {
// allocate a new node
Node *newNode = malloc ( sizeof(Node) );
newNode->first = malloc ( strlen(first) + 1 );
strcpy(newNode->first, first);
newNode->last = malloc ( strlen(last) + 1 );
strcpy(newNode->last, last);
newNode->number = num;
//make placeholder nodes
Node *a = list->headFirstName;
Node *b = list->headLastName;
// add this new node at the head of the "byFirst" list
if (strcmp(newNode->first, a->first) < 0) {
newNode->nextFirst = list->headFirstName;
list->headFirstName = newNode;
}
for (Node *i = list->headFirstName; i; i = i->nextFirst) {
// add after less alphabetical nodes
if (strcmp(newNode->first, i->first) > 0) {
newNode->nextFirst = i->nextFirst;
i->nextFirst = newNode;
}
// return error for duplicate name
if (strcmp(newNode->first, i->first) == 0 && strcmp(newNode->last, i->last) == 0) {
printf("That person is already in the list! Please try with a different name.\n");
}
}
// add this new node at the head of the "byLast" list
if (strcmp(newNode->last, b->last) < 0) {
newNode->nextLast = list->headLastName;
list->headLastName = newNode;
}
for (Node *j = list->headLastName; j; j = j->nextLast) {
// add after less alphabetical nodes
if (strcmp(newNode->last, j->last) > 0) {
newNode->nextLast = j->nextLast;
j->nextLast = newNode;
}
}
// return the multi-list object with updated or original head pointers
return list;
}
I figured out what my problem was. I had to add return list; to the end of each if statement otherwise the function attempts to perform every true statement; which causes the seg fault. In hindsight I'm surprised I didn't figure this out sooner.

Split linked list into half

I am trying to create a function splitlist(), which will split a singly linked list into two sublists – one for the front half, and one for the back half. I have come up with a code below which will work for the first time that I call the function, but when I call the function repeatedly, the program crashes. Any advice on how I can change my code to prevent such an error? The function splitlist() is void as it prints two lists which contains frontList and backList.
typedef struct _listnode {
int item;
struct _listnode *next;
} ListNode;
typedef struct _linkedlist {
int size;
ListNode *head;
} LinkedList;
void splitlist(LinkedList* list1, LinkedList * firsthalf, LinkedList *secondhalf)
{
ListNode *cur = list1->head;
ListNode *front = firsthalf->head;
ListNode *back = secondhalf->head;
int totalnodes = list1->size;
int i;
if (totalnodes % 2 != 0) //if odd number of elements, add 1 to make it easier for traversal of list
{
totalnodes = totalnodes + 1;
}
int halfnodes = totalnodes / 2;
{
for (i = 0; i < halfnodes; i++)
{
if (firsthalf->head == NULL) //initialise the head
{
firsthalf->head = malloc(sizeof(ListNode)); //create first node
front = firsthalf->head;
}
else
{
front->next = malloc(sizeof(ListNode));
front = front->next;
}
front->item = cur->item; // insert value from list1 into firsthalf
cur = cur->next; //point to next node in list1
}
front->next = NULL; //last node
for (i = halfnodes; i < totalnodes; i++)
{
if (secondhalf->head == NULL)
{
secondhalf->head = malloc(sizeof(ListNode));
back = secondhalf->head;
}
else
{
back->next = malloc(sizeof(ListNode));
back = back->next;
}
back->item = cur->item;
cur = cur->next;
}
back->next = NULL;
}
}
There are many things wrong with this code. First of all malloc return values are not checked, malloc can fail. And i strongly suspect that because of malloc fail your programm stops. You repeatedly allocate the memory inside the function, but do you free it when you do not need it anymore? Why do yo use malloc at all?
As posted earlier you do not need to.
Please post how the function is called, because it is really unclear how LinkedList* list1, LinkedList * firsthalf, LinkedList *secondhalf are used. Also it is unclear what is the structure of LinkedList is.
why use malloc?It will create a new list.But we want to split the list.
I guess firsthalf and second half are NULL
void splitlist(LinkedList* list1, LinkedList * firsthalf, LinkedList *secondhalf)
{
ListNode *cur = list1->head;
ListNode *front;
int totalnodes = list1->size;
int i;
if (totalnodes % 2 != 0) //if odd number of elements, add 1 to make it easier for traversal of list
{
totalnodes = totalnodes + 1;
}
int halfnodes = totalnodes / 2;
firsthalf->head=list1->head;
front=firsthalf->head;
for(i=0;i<halfnode;i++)
front=front->next;
secondhalf->head=front->next;
front->next=NULL;
}
At first glance I can't see much wrong with your code (assuming the assignment is to create copies of the list nodes in the new half lists), so the error could be in how you call the function, as an exmple, that could be:
LinkedList mainlist= {0};
LinkedList firsthalf= {0}, secondhalf= {0};
//mainlist got filled somehow; we now want to split
firsthalf->List= malloc(sizeof(ListNode));
secondthalf->List= malloc(sizeof(ListNode));
memset(firsthalf->List, 0, sizeof(ListNode));
memset(secondhalf->List, 0, sizeof(ListNode));
splitlist(&mainlist, &firsthalf, &secondhalf);

Resources