C Segmentation Fault - linked lists [closed] - c

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
I just started programming in C a few weeks ago and I am getting a segmentation fault in my program.
I believe it is because of those lines:
for (int i =0; i < HOW_MANY; i++)
{
people = people -> next;
if (people -> next == NULL) return people;
} //for
}//else
Here is my C program with comments based on my psedocode
struct person *insert_end (struct person *people, char *name, int age) {
//create a new space for the new person
struct person *pointer = malloc(sizeof(struct person));
// check it succeeded
if(pointer == NULL)
{
printf("The program could not allocate memory ");
exit(-1);
}
// set the data for the new person
strcpy(pointer -> name, name);
pointer -> age = age;
pointer -> next = people;
// if the current list is empty
if (people == NULL)
{
// set the new person's "next" link to point to the current list"
pointer -> next = people;
// return a pointer to the new person
return pointer;
}
else
{
// we need a loop to find the last item in the list
// (the one which as a "next" link of NULL)
for (int i =0; i < HOW_MANY; i++)
{
// set the "next link of this item to point
// to the new person, so that the person
// becomes the last item in the list
// (the next person should have a "next" link of NULL)
people = people -> next;
if (people -> next == NULL)
return people;
} //for
}//else
// return the start of the list
return pointer;
}
Also, let me know in case you need my full C code for the program, since this is only a method
Thank you,
Sarah :)

This is totally a question for Code Review, but since you did ask a question regarding how to implement an algorithm, at least I think it is suitable for Stack Overflow.
For starters, this is just a syntax thing, but I think you should replace this:
(*pointer).age = age;
with this
pointer -> age = age;
This notation is a lot cleaner in my opinion, and was incorporated into C for the purpose of accessing elements from a struct pointer. Other than that the code you have so far is missing some closing braces. Here's a version of your code with proper syntax, and other important stuff (such as what #H2CO3 mentioned):
struct person *insert_end (struct person *people, char *name, int age) {
//create a new space for the new person
struct person *pointer = malloc(sizeof(struct person));
// check it succeeded
if(pointer == NULL)
{
printf("The program could not allocate memory ");
exit(-1);
}
// set the data for the new person
strcpy(pointer -> name, name);
pointer -> age = age;
pointer -> next = people;
// if the current list is empty
if (person == 0)
{
// set the new person's "next" link to point to the current list"
pointer -> next = people;
// return a pointer to the new person
return pointer;
}
else
{
// we need a loop to find the last item in the list
// (the one which as a "next" link of NULL)
for (int i =0; i < HOW_MANY; i++)
{
// set the "next link of this item to point
// to the new person, so that the person
// becomes the last item in the list
// (the next person should have a "next" link of NULL)
lastItem -> next = people;
}
// return the start of the list
return pointer;
}
}
Lastly, since I don't have a full copy of your entire code, nor do I want it, I can provide you advice on how to implement the portion of pseudo-code you are unsure of.
use a loop to find the last item in the list (i.e. the one which has a
"next" link of NULL)
Finding the last item in a linked list is simple. The following function I have crafted below shows how you can obtain a specific node in a linked list at a certain index:
NODE* getNode(NODE* start, int index)
{
int i;
for(i = 0; i < index; i++)
{
start = start -> next;
}
return start;
}
This code can be modified so it instead returns the last node.
NODE* getLastNode(NODE* start)
{
for(;;)
{
start = start -> next;
if(start -> next == NULL)
return start;
}
}
The code above traverses each node in the list until it hits one that has it's connected node as NULL. It then returns that last node.
Now you can call said function above and get the last node.
set the "next" link of this item to point to the new person so the new
person becomes the last item in the list (i.e. the new person should
have a "next" link of NULL)
I'm sure you know how to do this judging from the code you provided above.

Related

Why is the program crashing after assigning a pointer [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 3 years ago.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Improve this question
Managing my linked-list is not working. My program just crashes after I set the value "next" to point on previous "head" of the list and after I search and find an element. What can I do?
The program compiles and starts, but it just crashes returning a random address from the memory.
I've tried to change the function to return the pointer of the new "head" instead of being void, but the result is the same.
By verifying where the program stops i found out that it stops doing "new node->next = (*head)" and on previous attempts with the instruction "return last".
I've tried changing almost completely the function just to understand the problem, but even if i pass a pointer to and already allocated list in the main the address does not work and it crashes.
Just to understand whats going on assume that the the program enters for sure in the if with the condition "type_temp=='A'"
This is the main:
#include"devices.h"
#include"stdio.h"
#include"string.h"
#include"stdlib.h"
#define LINE_LENGTH 80
#define COMMAND_LENGTH 30
int main(int argc, char** argvs)
{
FILE *fp;
struct Type_A** devices_A = NULL;
struct Type_B** devices_B = NULL;
struct Type_C** devices_C = NULL;
struct Request_Type_C** requests_devices_C = NULL;
int system_power, usable_power,
solar_system_power,solar_system_power_temp;
int id_temp,power_level_temp;
int power_level_normal_temp, power_level_low_temp;
char type_temp;
char file_line[LINE_LENGTH], command[COMMAND_LENGTH];
char *sub_line;
fp = fopen("input1.txt","r");
if(fp == NULL)
{
printf("Error opening the file\n");
return 1;
}
if(fgets(file_line,sizeof(file_line),fp) == NULL)
{
printf("Error reading the first line\n");
return 1;
}
sub_line = strtok(file_line, " ");
strcpy(command,sub_line);
sub_line = strtok(NULL, " ");
system_power = atoi(sub_line);
usable_power = system_power;
while(fgets(file_line,sizeof(file_line),fp) != NULL)
{
sub_line = strtok(file_line, " ");
strcpy(command,sub_line);
if(strcmp(command,"DEVICE_CONNECTED") == 0)
{
sub_line = strtok(NULL, " ");
id_temp = atoi(sub_line);
sub_line = strtok(NULL, " ");
type_temp = *sub_line;
if(type_temp == 'A')
{
sub_line = strtok(NULL, " ");
power_level_normal_temp = atoi(sub_line);
sub_line = strtok(NULL, " ");
power_level_low_temp = atoi(sub_line);
//function with the problem 1
add_device_a(devices_A,id_temp,type_temp,
power_level_normal_temp,power_level_low_temp,0,0);
//function with the problem 2
add_device_a_to_system(devices_A,id_temp,&usable_power);
}
.
.
.
This is the first function with the problem:
void add_device_a(struct Type_A** head, int id, char type,
int power_level_normal, int power_level_low,
int connected, int consume)
{
//allocate the new node
struct Type_A *new_node = (struct Type_A*) malloc(sizeof(struct Type_A));
//put in the data
new_node->id = id;
new_node->type = type;
new_node->power_level_normal = power_level_normal;
new_node->power_level_low = power_level_low;
new_node->connected = connected;
new_node->consume = consume;
//setting the next of the new node
new_node->next = (*head); <-instruction that generates the problem
crashing the program
//move the head to point to the new node
(*head) = new_node;
return;
} //end add_device_a
This is the second function that generates the problem:
int add_device_a_to_system(struct Type_A** head, int id, int* usable_power)
{
struct Type_A *node = find_device_a_by_id(head, id);` <-instruction
that generates the problem
.
.
.
The true function that generates the problem
struct Type_A *find_device_a_by_id(struct Type_A** head, int id)
{
//used for traverse the list
struct Type_A *last = *head;
//check if last is truly the last node
while(last != NULL)
{
//if the id is equal return the pointer to that node
if(last->id == id) {
return last; **<-instruction that makes the program crash**
}
//else keep going with the next
last = last->next;
}
//didn't find an id equal to the one passed ad parameter
return NULL;
}//end of *find_device_a_by_id
Adding an element to the list should add the element on the linked list as first and modify the pointer to that entry.
Finding an element should return a pointer to that specific entry on the linked-list.
You are defining a pointer to a pointer to your struct:
struct Type_A** devices_A = NULL;
Right now it does not point to a valid struct Type_A* as you haven't created one to point it to. So when you pass this into your function add_device_a and it dereferences that variable, you invoke undefined behavior, in this case probably a segfault.
What you probably actually want is to define struct Type_A* devices_A = NULL; And then when you pass it into your function, pass in the address of that pointer:
add_device_a(&devices_A,id_temp,type_temp,
power_level_normal_temp,power_level_low_temp,0,0);
You probably want to declare devices_A as struct Type_A* devices_A = NULL (not as a double pointer), and then call add_device_a() using add_device_a(&devices_A, ....
The way you're doing it is confusing and requires that you allocate space for devices_A, also in this context dereferencing *head when devices_A is NULL is making the program read from address NULL which is undefined behavior and may cause crashes.
You can also see this question about how to manage linked lists properly.

A pointer points to a NULL pointer

code from cs50 harvard course dealing with linked list:
---The problem I do not understand is that when node *ptr points to numbers, which is a null pointer, how can the for loop: (node *ptr = numbers; ptr != NULL) run at all since *numbers = NULL?---
full version of the codes can be found at: https://cdn.cs50.net/2017/fall/lectures/5/src5/list2.c
#include <cs50.h>
#include <stdio.h>
typedef struct node
{
int number;
struct node *next;
}
node;
int main(void)
{
// Memory for numbers
node *numbers = NULL;
// Prompt for numbers (until EOF)
while (true)
{
// Prompt for number
int number = get_int("number: ");
// Check for EOF
if (number == INT_MAX)
{
break;
}
// Check whether number is already in list
bool found = false;
for (node *ptr = numbers; ptr != NULL; ptr = ptr->next)
{
if (ptr->number == number)
{
found = true;
break;
}
}
The loop is to check for prior existence in the list actively being built. If not there (found was never set true), the remaining inconveniently omitted code adds it to the list.
On initial run, the numbers linked list head pointer is null, signifying an empty list. That doesn't change the algorithm of search + if-not-found-insert whatsoever. It just means the loop is never entered because the bail-case is immediately true. in other words, with numbers being NULL
for (node *ptr = numbers; ptr != NULL; ptr = ptr->next)
the condition to continue, ptr != NULL is already false, so the body of the for-loop is simply skipped. That leads to the remainder of the code you didn't post, which does the actual insertion. After that insertion, the list now has something, and the next iteration of the outer-while loop will eventually scan the list again after the next prospect value is read. This continues until the outer-while condition is no longer satisfied.
A Different Approach
I have never been fond of the cs50 development strategy, and Harvard's technique for teaching C to entry-level CS students. The cs50 header and lib has caused more transitional confusion to real-world software engineering than one can fathom. Below is an alternative for reading a linked list of values, keeping only unique entries. It may look like a lot, but half of this is inline comments describing what is going on. Some of it will seem trivial, but the search-and-insert methodology is what you should be focusing on. It uses a strategy of pointer-to-pointer that you're likely not familiar with, and this is a good exposure.
Enjoy.
#include <stdio.h>
#include <stdlib.h>
struct node
{
int value;
struct node *next;
};
int main()
{
struct node *numbers = NULL;
int value = 0;
// retrieve list input. stop when we hit
// - anything that doesn't parse as an integer
// - a value less than zero
// - EOF
while (scanf("%d", &value) == 1 && value >= 0)
{
// finds the address-of (not the address-in) the first
// pointer whose node has a value matching ours, or the
// last pointer in the list (which points to NULL).
//
// note the "last" pointer will be the head pointer if
// the list is empty.
struct node **pp = &numbers;
while (*pp && (*pp)->value != value)
pp = &(*pp)->next;
// if we didn't find our value, `pp` holds the address of
// the last pointer in the list. Again, not a pointer to the
// last "node" in the list; rather the last actual "pointer"
// in the list. Think of it as the "next" member of last node,
// and in the case of an empty list, it will be the address of
// the head pointer. *That* is where we will be hanging our
// new node, and since we already know where it goes, there is
// no need to rescan the list again.
if (!*pp)
{
*pp = malloc(sizeof **pp);
if (!*pp)
{
perror("Failed to allocate new node");
exit(EXIT_FAILURE);
}
(*pp)->value = value;
(*pp)->next = NULL;
}
}
// display entire list, single line
for (struct node const *p = numbers; p; p = p->next)
printf("%d ", p->value);
fputc('\n', stdout);
// free the list
while (numbers)
{
struct node *tmp = numbers;
numbers = numbers->next;
free(tmp);
}
return EXIT_SUCCESS;
}
This approach is especially handy when building sorted lists, as it can be altered with just a few changes to do so.
If you examine rest of the code which is also within the while loop, you can see alteration of numbers on the shared link.
if (!found)
{
// Allocate space for number
node *n = malloc(sizeof(node));
if (!n)
{
return 1;
}
// Add number to list
n->number = number;
n->next = NULL;
if (numbers)
{
for (node *ptr = numbers; ptr != NULL; ptr = ptr->next)
{
if (!ptr->next)
{
ptr->next = n;
break;
}
}
}
else
{
numbers = n;
}
}
Besides, it doesn't hit body of the for loop at first, so your thinking is correct.

C - Moving or deleting multiple nodes based on value

I am trying to make a program for Santa! My knowledge is limited; I am lost in pointers and loops etc, I've been thinking this for a couple hours.
I have an array of pointers to singly linked lists. Each array index indicates a list of children in age groups 0: 0-3, 1: 4-7, 2: 8-11, 3: 11 - 15.
Each child is a struct, now after each year I want to go through all the lists, increment their age by 1, and if they need to change an age group, I have to move the node to the appropriate list that holds that age group. If although the child gets over the age of 15 then it has to delete the node instead. My code is incomplete because I am new to linked lists and I got confused.
My main issue is that I make changes to the lists as I move through them, so if I check for the first node and I remove it, I have to check the first node again because now its a new one, so I keep checking until the Head is okay, is this the right approach? I am not sure my code works, I cant test it yet.
the part from my Santa_Claus.h:
/*Structure defining a node of the children list*/
struct child {
int cid; /*The identifier of the child.*/
int age; /*The age of the child.*/
int did; /*The identifier of the child.*/
int present_choices[M]; /*The array in which the preferences of the child for presents are stored*/
struct child *next; /* Singly-linked, sorted by id */
};
the part from Santa_Claus.c
#define N 4 /*Number of children's age categories*/
struct child *Age_categories[N];
int new_season(void) {
int i;
struct child *childP = NULL;
struct child *prev = NULL;
struct child *childptr = NULL;
int MaxAges[N] = {3,7,11.15};
//Increment Age Loop
for(i = 0; i < N; i++){
childP = Age_categories[i];
while(childP != NULL){
childP->age = childP->age + 1;
childP = childP->next;
}
}
//Remove or Move Loop
for(i = 0; i < N; i++){
childP = Age_categories[i];
//while the first is still > than the max age of this category
while(childP->age > MaxAges[i]){
if(i != (N-1)){
childP->next = Age_categories[i+1];
Age_categories[i+1] = childP;
}else{
Age_categories[i] = childP->next;
}
childP = childP->next;
}
prev = Age_categories[i];
childP = prev->next;
while(childP != Null){
if(childP->age > MaxAges[i]){
if(i != (N-1)){
prev->next = childP->next;
childP->next = Age_categories[i+1];
Age_categories[i+1] = childP;
}else{
Age_categories[i] = childP->next;
}
}
prev = childP;
childP = childP->next;
}
}
return 1;
}
My main issue is that I make changes to the lists as I move through them, so if I check for the first node and I remove it, I have to check the first node again because now its a new one, so I keep checking until the Head is okay, is this the right approach?
In place edits will bite you at the edge-cases. You'll have problems with the head and the tail nodes. If you remove the head, then whatever was keeping track of your head now points to nullspace.
In short, you just have to be careful. There are a lot of ways to screw it up. And for newbies, I'd suggest you stay away from pointers in C. Powerful, but a pain in the ass. Stick to fixed arrays unless you really need this thing to scale.
You don't need to re-check the head so much as have a special case to check the head, and do head-safe edits. Also checking if the set is empty, and checking the tail are typically good ideas. You can try and figure out something clever to avoid that sort of thing and have smooth code that handles it all... But clever code often bites you in the ass just as much.
My main issue is that I make changes to the lists as I move through
them, so if I check for the first node and I remove it, I have to
check the first node again because now its a new one, so I keep
checking until the Head is okay, is this the right approach?
In principle it's the right approach, but you have to do that not only for the head, but for every node in each list. Your code works not in all cases, e. g.
//while the first is still > than the max age of this category
while(childP->age > MaxAges[i]){
if(i != (N-1)){
childP->next = Age_categories[i+1];
Age_categories[i+1] = childP;
}else{
Age_categories[i] = childP->next;
}
childP = childP->next;
}
constitutes an endless loop if let's say the head child in age group 0 has become 4 years old.
Consider this correct code, which is shorter and therefore gives less opportunity for mistakes:
// Remove or Move Loop
for (i = 0; i < N; i++)
{
struct child **chPA; // address of pointer to node under check
// for each node in age group i
for (chPA = &Age_categories[i]; childP = *chPA; )
if (childP->age > MaxAges[i])
{
*chPA = childP->next; // remove node from this list
if (i != N-1)
{
struct child **chPA; // address of pointer to node in next group
// find where in next group to insert the aged node
for (chPA = &Age_categories[i+1]; childptr = *chPA;
chPA = &childptr->next)
if (childptr->cid > childP->cid) break;
(*chPA = childP)->next = childptr;
}
else
free(childP); // delete the grown out node
}
else
chPA = &childP->next; // proceed to next node
}
(It inserts a node in the next higher age group according to its cid because of your code comment
struct child *next; /* Singly-linked, sorted by id */

Need help checking a linked list in C

I'm having a issue on searching through a linked list. I'm making a grade book program and I'm doing input error checks to see if the user entered a existing course to enroll a student to said course.
So this is the struct for the course info with a doubly linked list.
typedef struct Course_Info // Course information
{
int Course_ID;
char Course_Name[15];
struct Course_Info *next;
} Course;
typedef struct // Linked list for Course
{
int Ctracker; // Keeps track of courses
Course *Course_Head;
Course *Course_Tail;
} List_Course;
And their corresponding variables along with initialization.
List_Student Students;
List_Course Courses;
Grade_List Grades;
Students.Stracker = 0;
Students.Student_Head = Students.Student_Tail = NULL;
Courses.Ctracker = 0;
Courses.Course_Head = Courses.Course_Tail = NULL;
Grades.Grade_cnt = 0;
Grades.Grade_Head = Grades.Grade_Tail = NULL;
In this function I'm going to enroll a student to a course but first im going to do some input checking to make sure the course exist.
void EnrollStudent(List_Course *Courses, List_Student *Students)
{
int CID; int SID;
printf("Enter course ID: ");
scanf("%d%*c", &CID);
if( CID != Courses -> Course_Head -> Course_ID)
{
printf("Course does not exist!\n");
return;
}
else
{
printf("Found class!\n");
}
}
The problem with what I currently have is that it only searches the first element of the linked list. How do I go about making a loop that checks the entire linked list?
Iterating a linked list is quite straightforward.
You need to use a local variable which is the current element of the list, which you init to Courses->Course_Head, eg:
Course* current = Courses->Course_Head;
then until current != NULL you just keep updating the current to point to the next element, eg:
while (current != NULL) {
// do what your want with current
current = current->next;
}
Mind that in your example you speak about a doubly linked list but it's a single linked list with two pointers to head and tail, a double linked list has two pointers for each node in both directions so that you can traverse it in reverse order, which is not the case in your situation.
ListCourse * current = Courses->Course_Head;
while ( (NULL != current) && (CID != current->Course_ID) ) current = current->next;
if (NULL == current) printf("Course %d not found\n", CID);
else printf("Course %d found\n", CID);
Your problem is that you are not iterating over the list, rather you are only checking the list head. You need to maintain a pointer to the node you are checking and iterate it (point it to the next node) in case you didn't find what you were looking for. You exit if there is nothing left to search or you found what you were looking for.

Linked List Assistance [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 8 years ago.
Improve this question
I'm working on a linked list in C with insert and print functions. However, I am getting a segmentation fault from the insert, and when I try to fix this I end up with one from the print function. Any help would be greatly appreciated.
typedef struct node
{
struct node *next;
double value;
} NodeT, *NodeTP;
int listSize = 0;
int insert(NodeT *firstEle, int value)
{
NodeTP temp;
if((temp = (NodeTP)malloc(sizeof(NodeT))) == NULL)
{
return 0;
}
// first node in the list.
if(listSize == 0)
{
firstEle->value = value;
firstEle->next = NULL;
}
// finds the end node and adds the new node to the list.
else
{
while(temp != NULL)
{
temp = temp->next;
}
temp->value = value;
temp->next = firstEle;
firstEle = temp;
}
listSize++;
return 1;
}
int print(NodeT List)
{
printf("Element: %.2f\n", List.value);
return 1;
}
int main(int argc, char* argv[])
{
// creates the list.
NodeTP list;
if((list = (NodeTP)malloc(sizeof(NodeT))) == NULL)
{
return 0;
}
list = NULL;
insert(list, 5);
print(list[0]);
insert(list, 15);
print(list[1]);
return EXIT_SUCCESS;
}
Print Issue
When it comes to the print statements, you are using an invalid syntax. You created a single pointer and allocated enough memory for that pointer and what it points to. You did not create an array of NodeT elements. As such, the list[x] will not work.
You will need to generate a function that will locate the 'x' element in the list you are making. You could include that in the print function you write. Just change it up to take the int of the element you want:
int print(NodeT head, int element) {}
Don't forget to check your bounds in case the element asked for is outside of your current range.
All you really need to do then is step through the elements to the desired one.
Insert Issue
In your if/else statement, why are you trying to iterate through 'temp'? 'temp' was created in this function and should have no other elements attached to it. You should be iterating through 'firstEle'. You also don't want to set firstEle = temp; as that will over write what was there before and you are now pointing to something else.
One way to simplify this code is to use a head and tail. The head never changes but the tail moves as elements get added. You can have 'insert' return the tail and when you insert a new element, just provide the tail and the new element will be added there, no iteration needed.
Memory Issue
While this will not be major for this program, I would adjust where I malloc my new node till after I have insured that it isn't the first element. Else, you have a block allocated that is never used. Or, if this is the first element to be added, free the block.
And Fred makes a good point. That will cause issues.
Multiple issues (the list is far from complete):
You're missing the include files for malloc() and printf().
You allocate memory in main(), but don't use it.
If you want to modify the list head in insert(), you have to pass a pointer to it.
You check if the memory allocation in insert() succeeds, but don't handle the case that it doesn't - so you can as well just get rid of it.
As you ignore the return values of both insert() and print() you could as well change their return types to void.
While searching for the last element of the list, you overwrite the pointer (temp) to the memory you just allocated.
When you think you've found the last element, you actually make the new element's next point to it and set the first element to point to the new element, which would make it effectively the first and not the last element (if all the other stuff would work...).
You're trying to use array style access with [] for the list. You have to use pointers.
This is a version where I fixed at least the most basic errors in order to make it compile and at least work. It's still far from perfect (or even acceptable), but I tried change as little as possible to make it easier for you to spot the differences and learn what you did wrong and further improve it from there.
#include <stdlib.h>
#include <stdio.h>
typedef struct node
{
struct node *next;
double value;
} NodeT, *NodeTP;
int listSize = 0;
int insert(NodeT **firstEle, int value)
{
NodeTP temp, lastEle;
temp = (NodeTP)malloc(sizeof(NodeT));
// first node in the list.
if(listSize == 0)
{
temp->value = value;
temp->next = NULL;
*firstEle = temp;
}
// finds the end node and adds the new node to the list.
else
{
for (lastEle = *firstEle;lastEle->next != NULL;lastEle = lastEle->next);
temp->value = value;
temp->next = NULL;
lastEle->next = temp;
}
listSize++;
return 1;
}
int print(NodeT List)
{
printf("Element: %.2f\n", List.value);
return 1;
}
int main(int argc, char* argv[])
{
// creates the list.
NodeTP list;
list = NULL;
insert(&list, 5);
print(*list);
insert(&list, 15);
print(*list->next);
return EXIT_SUCCESS;
}

Resources