I'm having this strange segmentation fault. I'm trying to find if a patients id already exists in the list of patients using pointers. I think the code in question is:
#include <stdio.h>
#include <stdlib.h>
#include "health.h"
void addPatient(int patientID) {
printf("here1");
Chart* patients_chart;
// find the patient with id
patients_chart = patientList;
while(patients_chart == NULL || patients_chart->id == patientID) {
patients_chart = patients_chart->next;
printf("here2");
}
printf("here3");
// if patient wasn't found, add new patient
if (patients_chart == NULL) {
Chart *new_chart;
printf("here4");
// allocate and initialize new patient
new_chart = (Chart*)malloc(sizeof(Chart));
new_chart->id = patientID;
new_chart->buffer = NULL;
// insert new patient into list
new_chart->next = patientList;
patientList = new_chart;
printf("here5");
}
}
The included health.h is just method declarations and structs. I will list them below, but please note that my assignment restricts me from modifying any of the code in health.h. I will also post my code at the very end.
/*
* Patient's health chart: ID + linked list of health type readings
*/
typedef struct chartEntry* Chartptr; /* pointer to a Chart */
typedef struct chartEntry{
int id; /* patient ID */
CBuffptr buffer; /* pointer to first health type buffer */
Chartptr next; /* pointer to next patient */
}Chart;
extern Chartptr patientList;
I call the function in main with input like this one: 1,12:12:12,7,0
The 7 is the "command"
the 1 is the patient id in question
You can ignore the rest.
I understand how to find the patient, but I'm getting this annoying seg fault. Thank you for your time!
The following code is buggy:
while(patients_chart == NULL || patients_chart->id == patientID) {
patients_chart = patients_chart->next;
printf("here2");
}
You are advancing for as long as either the pointer is NULL or the pointer matches the patient ID. You're missing a negation there. Instead, use:
while(patients_chart != NULL && patients_chart->id != patientID) {
patients_chart = patients_chart->next;
printf("here2");
}
while(patients_chart == NULL || patients_chart->id == patientID) {
patients_chart = patients_chart->next;
}
Here if Condition 1 (patients_chart == NULL) is true, then you do this:
patients_chart = patients_chart->next;
which is Null Pointer Dereferencing, thus causing Seg Fault.
Related
I have a linked list and two functions - one to add and other to delete members from the list.
When I try to delete a member I am getting an exception in the delete function.
The exception is in line
if (strcmp((temp_person_ptr->name), name_to_remove))
The exception says -> Unhandled exception at 0x50C4EF18 (ucrtbased.dll) in singly_linked_list.exe: 0xC0000005: Access violation reading location 0xDDDDDDDD. occurred
Full program is below -
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include "globals.h"
extern struct person* starting_address;
struct person* delete_person(size_t* list_size_ptr, struct person* ptr_current_person)
{
printf("There are %u items in list \n", *list_size_ptr);
struct person* temp_person_ptr = starting_address, *to_save_person_ptr;
printf("The items in list are \n");
while (temp_person_ptr)
{
printf("%s \n", temp_person_ptr->name);
temp_person_ptr = temp_person_ptr->next_person_ptr;
}
char name_to_remove[MAX_NAME_LENGTH];
printf("Please enter the name to remove \n");
gets_s(name_to_remove, MAX_NAME_LENGTH - 1);
temp_person_ptr = starting_address;
to_save_person_ptr = starting_address; // Not required by logic - just to initialize for compiler
while (temp_person_ptr)
{
if (strcmp((temp_person_ptr->name), name_to_remove))
{
to_save_person_ptr = temp_person_ptr;
temp_person_ptr = temp_person_ptr->next_person_ptr;
}
else
{
// Since we are going to remove temp_person_ptr - we save it's next person_ptr in preceding person which is to_save_person_ptr
// Only if the person_ptr to be removed is NOT the first person
// For now - assume - one element name will match
if (temp_person_ptr != starting_address)
to_save_person_ptr->next_person_ptr = temp_person_ptr->next_person_ptr; // takes care if temp_person_ptr is the last one as well
else // Else the next person's address is the new starting address
starting_address = temp_person_ptr->next_person_ptr;
free(temp_person_ptr);
(*list_size_ptr)--;
}
}
return (ptr_current_person);
}
The part to add an element to the list looks as below (entire function) -
struct person* add_person(size_t* list_size_ptr, struct person* ptr_current_person)
{
struct person *ptr_new_person;
ptr_new_person = (struct person*) malloc(sizeof(struct person));
// If first person- its starting address is the starting address of list
if ((*list_size_ptr) == 0)
{
starting_address = ptr_new_person;
}
else
{
//1. Add the new address to the chain - only if this is not the first person
ptr_current_person->next_person_ptr = ptr_new_person;
}
ptr_new_person->next_person_ptr = NULL;
printf("Please enter the name \n");
gets_s(ptr_new_person->name, MAX_NAME_LENGTH - 1);
// 2. We may make ptr_new_person as ptr_current_person
ptr_current_person = ptr_new_person;
// 3. Now onwards ptr_current_person refers to the pointer to the newly added person
(*list_size_ptr)++;
return (ptr_current_person);
}
while (temp_person_ptr)
{
if (strcmp((temp_person_ptr->name), name_to_remove))
{
to_save_person_ptr = temp_person_ptr;
temp_person_ptr = temp_person_ptr->next_person_ptr;
}
else{...}
From this snippet it looks like temp_person_ptr is pointing to something, but the ->name is NULL. Add a printf statement like this before your strcmp:
if(!(temp_person_ptr->name)){
printf("This is why your segfaulting\n");
}
And you will either see that ->name is null or its something else. good luck
You haven't posted enough to get a proper analysis and help. The guidance for making a complete, minimal example isn't just for grins; often by reducing a problem to its core, the programmer finds their error themselves. If not, it gives a great place for other to. Regardless, the loop in your delete is essentially:
temp = start;
while (temp) {
if (strcmp(temp->name, name)) {
temp = temp->next;
} else {
free(temp);
}
}
Your free should likely have been something like:
void *p = temp->next;
free(temp);
temp = p;
It worked, I was not exiting the while loop after free and going on all the way.
I added a return to return after free is done and list size is decremented. It works now.
My code doesn't adding second node to tree. It gives me SIGSEGV fault when i'm adding the second node.I think its about strcmp function but when i'm trying to understand how it works properly at the very bottom of main func it returns -1 so i've wrote it like this.And most of my variables named Turkish so here are the translations of them to make you understand more easily
dugum=node,kok=root;sol=left;sag=right;anne=mother
// C program to demonstrate insert operation in binary search tree
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tree {
char *harf;
char *morskodu;
struct tree *left;
struct tree *right;
} agac;
agac *kok = NULL;
void ekle(char *harf, char *morskodu) {
if (kok == NULL) {
kok = (agac *)malloc(sizeof(agac));
kok->harf = harf;
kok->morskodu = morskodu;
kok->left = NULL;
kok->right= NULL;
} else {
agac *yeni = (agac *)malloc(sizeof(agac));
yeni->harf = harf;
yeni->morskodu = morskodu;
yeni->left = NULL;
yeni->right = NULL;
agac *dugum = kok, *anne;
while (dugum != NULL) {
anne = dugum;
if (harf <= dugum->harf)
dugum = dugum->left;
else
dugum = dugum->right;
}
if (harf <= dugum->harf)
anne->left = yeni;
else
anne->right = yeni;
}
}
void dolas(agac *dugum) {
if (dugum != NULL) {
printf(" %s ", dugum->harf);
dolas(dugum->left);
dolas(dugum->right);
}
}
void main() {
ekle("a", "-");
ekle("b", "-.");
dolas(kok);
int x = strcmp("A", "B");
printf("%d", x);
}
You try to dereference a NULL pointer.
while (dugum != NULL) {
anne = dugum;
if (harf <= dugum->harf)
dugum = dugum->sol;
else
dugum = dugum->sag;
}
This loop ends when dugum is NULL.
Directly after you try to access dugum->harf:
if (harf <= dugum->harf)
This leads to undefined behavior.
Also note that this comparisons compare the pointers to string literals, and is therefore also undefined behavior. To compare two C strings you should use strcmp.
I'm writing a function (*rwObjects()) that will read a formatted file and save it's strings, one object at a time. Unfortunately, it's for my studies which have a restriction - stdio.h, stdlib.h and string.h is practically all I can use.
Here's the problem: whenever I run the code, when it gets to the fclose(input), VS17 says my project's triggered a breakpoint, and then opens a tab that says "wntdll.pdb not loaded" or something.
The question is: how do I not trigger the breakpoint and close the file properly? Or, if the problem isn't in the file, where is it?
Code (C):
#define _CRT_SECURE_NO_WARNINGS
#define cnCOUNTRY_LENGTH 3
#define cnOBJECT_NAME_LENGTH 30
#define cnOBJECT_MAX 1000
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//--Поле объекта objectType--
//Country (0) - строка названия страны
//ObjectName (1) - строка названия объекта
//Square (2) - площадь объекта
//Error (3) - ошибка в содержании строки
typedef enum IOOptions {Country, ObjectName, Square, Error} IOType;
//--Тип обрабатываемых объектов--
//char Country - строка названия страны
//char ObjectName - строка названия объекта
//int Square - площадь объекта
typedef struct object {
char Country[cnCOUNTRY_LENGTH];
char ObjectName[cnOBJECT_NAME_LENGTH];
int Square;
} objectType;
//--Копирование текущего элемента строки objects.txt--
//strMod - Строка, в которую идёт копирование
//strPos - Позиция в считываемой строке
//strBlueprint - Строка, из которой идёт копирование
//writeType - Поле объекта objectType. При "Country" - переводит вводимые символы в верхний регистр ('a' -> 'A' и т.д.)
void copyInputStr(char *strMod, int *strPos, char *strBlueprint, IOType writeType) {
for (*strPos; *strBlueprint != ' ' && *strBlueprint != '\n' && *strBlueprint != NULL; *strPos = *strPos + 1) {
*strMod = *strBlueprint;
if (writeType == Country) toupper(*strMod);
strBlueprint++; strMod++;
}
}
//--Запись текущего элемента строки objects.txt в текущий объект--
//strInput - Строка, из которой идёт запись
//objectOutput - Объект, в который идёт запись
//writeType - Поле объекта, в которое идёт запись
void writeObject(char *strInput, objectType *objectOutput, IOType writeType) {
if (writeType == Country)
strcpy(objectOutput->Country, strInput);
else if (writeType == ObjectName)
strcpy(objectOutput->ObjectName, strInput);
else if (writeType == Square)
objectOutput->Square = atoi(strInput);
else printf("Error 1. Invalid parameters");
}
//--Чтение objects.txt и запись в массив objectType--
//Возвращает указатель на первый элемент массива объектов
objectType *rwObjects() {
FILE *input = fopen("objects.txt", "r");
char objectQttStr[4], objectStr[38];
fgets(objectQttStr, 4, input);
objectType *objectList = (objectType *)malloc(atoi(objectQttStr)), *currentObject = objectList;
currentObject = (objectType *)malloc(atoi(objectQttStr));
for (int i = 0; i < atoi(objectQttStr); i++) {
fgets(objectStr, 38, input);
IOType inputType = Country;
for (int j = 0; objectStr[j] != NULL && objectStr[j] != '\n'; j++) {
char strBuf[cnOBJECT_NAME_LENGTH];
memset(&strBuf, 0, sizeof(strBuf));
copyInputStr(&strBuf, &j, &objectStr[j], inputType);
writeObject(&strBuf, currentObject, inputType);
inputType++;
}
currentObject++;
}
fclose(input); //this is where it happens
return objectList;
}
void main() {
objectType *objectList = rwObjects();
printf("");
}
This is one confusing program, but I've found no other way to conform the bloody rules, so let's put the coding style aside, shall we?
Also, I know that should it run successfully, nothing would happen - that is by design. It's not finished yet.
EDIT: Don't worry about validity of input data. All the input data formatting is explicitly stated in the task, ergo no checks are reqired. Still, for the curious, here it is:
objects.txt:
3
USA WelfareArrangement 120
Rus PoiskZemli 30
usa asdfEstate 1
EDIT 2: The moment I stopped using malloc, everything was fine. The question is - why exactly was it such a problem, and how would I create an array of the exact size I need, instead of creating max size evey time, if not with malloc?
First problem:
objectType *objectList = (objectType *)malloc(atoi(objectQttStr)), *currentObject = objectList;
currentObject = (objectType *)malloc(atoi(objectQttStr));
The malloc function allocates a given number of bytes. So if you have 5 objects, you only allocate 5 bytes. That's not enough for your structs. This results in you writing past the end of allocated memory which invokes undefined behavior.
If you want it to allocate space for a specific number of objects, you need to multiply by the object size:
objectType *objectList = malloc(sizeof(*objectList)*atoi(objectQttStr));
Also, don't cast the return value of malloc.
You also assign currentObject to the same value as objectList but then overwrite it with a separate memory allocation. So get rid of the second malloc.
Second problem:
memset(&strBuf, 0, sizeof(strBuf));
copyInputStr(&strBuf, &j, &objectStr[j], inputType);
writeObject(&strBuf, currentObject, inputType);
Your copyInputStr and writeObject function expect a char *, but you pass in the address of the strBuf array which has type char (*)[30]. Get rid of the address-of operator here:
memset(strBuf, 0, sizeof(strBuf));
copyInputStr(strBuf, &j, &objectStr[j], inputType);
writeObject(strBuf, currentObject, inputType);
Third problem:
void copyInputStr(char *strMod, int *strPos, char *strBlueprint, IOType writeType) {
for (*strPos; *strBlueprint != ' ' && *strBlueprint != '\n' && *strBlueprint != NULL; *strPos = *strPos + 1) {
*strMod = *strBlueprint;
if (writeType == Country) toupper(*strMod);
strBlueprint++; strMod++;
}
}
When you copy the characters in strMod, you're not adding a null byte at the end. A string in C is a null-terminated array of characters, so what you end up with is not a string but just an array of characters. When you later call strcpy on this array, the function doesn't find a null byte so it keeps reading until it does. This causes the function to read uninitialized bytes and/or read past the end of the array, which again invokes undefined behavior.
Add the null terminating byte after the loop. Also, the result of the toupper function isn't assigned to anything, so it does nothing. You need to assign it back to *strMod:
void copyInputStr(char *strMod, int *strPos, char *strBlueprint, IOType writeType) {
for (*strPos; *strBlueprint != ' ' && *strBlueprint != '\n' && *strBlueprint != NULL; *strPos = *strPos + 1) {
*strMod = *strBlueprint;
if (writeType == Country) *strMod = toupper(*strMod);
strBlueprint++; strMod++;
}
*strMod = 0;
}
I could really use some help with an address book program I've been struggling on for days now. I'm working with a doubly linked list in C. I'm trying to add nodes into the list at user-entered positions, starting with position 0. The positions will not be entered out of range. (no inserts at position 1 before something at position 0 etc.) The positions can be repeated though: inserting the new node in the position before the previous position occupant. (for example: if position 1 has x, and new node is inserted at position 1 with y, position 1 now has y and position 2 has x)
I need to take the user entered position number and retrieve the current person in that position, but I can't quite get it right. Also, I have included my insert function if you wanted to take a look at that as well because it isn't working properly either. Thanks for any help!
EDIT: The main problem right now is that my code for finding pPersonCur is failing when position == 1. Also, the insert function is not entering things in the proper order (the newest insertion in a position does not displace the older insertion correctly). The broken pPersonCur code makes it hard to diagnose why exactly this is, however.
addressbook.h excerpt:
typedef struct person Person;
struct person {
char lastName[255];
char firstName[255];
char email[255];
char phoneNumber[255];
Person *pNext;
Person *pPrev;
};
addressbook.c excerpt:
#include "addressbook.h"
Person * InsertPerson(Person * pPersonCur) {
Person * pPersonNew;
/* data gathered for CreatePerson() function here */
pPersonNew = CreatePerson(pLastName, pFirstName, pEmail, pPhoneNumber);
if (pPersonCur)
{
pPersonNew->pNext = pPersonCur;
pPersonNew->pPrev = pPersonCur->pPrev;
pPersonCur->pPrev = pPersonNew;
if (pPersonNew->pPrev)
pPersonNew->pPrev->pNext = pPersonNew;
} else
{
pPersonNew->pPrev = pFirst;
pPersonNew->pNext = NULL;
if (pFirst)
pFirst->pNext = pPersonNew;
}
return (pPersonNew);
}
main.c excerpt:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "addressbook.h"
Person *pFirst; /* First name in list */
int main(void) {
Person *pPersonCur = NULL; /* current Person */
int bDone = 0, position = 0, counter = 0;
pFirst = NULL;
printf("Ready\n");
while (!bDone) {
char input = getchar();
switch (input) {
case 'a':
counter = 0;
scanf("%d", &position); /* Where desired position is entered */
if (position == 0) {
if (pFirst) {
if (pFirst->pNext) {
pPersonCur = pFirst->pNext;
}
} else {
pPersonCur = pFirst;
}
} else {
pPersonCur = pFirst->pNext;
while (counter < position) {
pPersonCur = pPersonCur->pNext;
counter++;
}
}
InsertPerson(pPersonCur); /* Takes in person at desired position, return value is new inserted person */
break;
/* Some other cases here */
case 'q':
bDone = 1;
break;
}
}
/* Rest of code */
It seems so that you never assign a value to pFirst.
When position is not 0 the line pPersonCur = pFirst->pNext; is executed and pFirst in this place is still NULL.
Add a condition to your insert function to check whether list's head is assigned.
Person * InsertPerson(Person * pPersonCur) {
. . .
else
{
pPersonNew->pPrev = pFirst;
pPersonNew->pNext = NULL;
if (pFirst)
pFirst->pNext = pPersonNew;
else
pFirst = pPersonNew; // If pFirst is not assigned, assign it to newly created person
}
return (pPersonNew);
}
Despite that, if you happen to call InsertPerson with NULL argument, your code would put new Person after the first one and cut the rest of the list off.
To put new Person to the end of the list when called with NULL you could use something like this in your InsertPerson function:
if(pFirst) {
Person *last = pFirst;
while(last->pNext != NULL) {
last = last->pNext;
}
last->pNext = pPersonNew;
pPersonNew->pPrev = last;
}
else
pFirst = pPersonNew;
Insertion according to position index might fail as well if you give a position index that is higher than there are nodes in the list. Some sort of safety check should be added as well.
pPersonCur = pFirst->pNext;
while (counter < position && pPersonCur->pNext != NULL) { // If last node reached, stop the loop
pPersonCur = pPersonCur->pNext;
counter++;
}
This implementation would add new Person to the end of the list if position index is too high.
I'm having trouble with my pointers. I'm trying to add multiple patients to my list. I know how to do it, it's just that the code gives me seg faults.
Here is the code in question:
void addPatient(int patientID) {
Chartptr patients_chart;
patients_chart = getChart(patientID);
// if patient wasn't found, add new patient
if (patients_chart == NULL) {
Chartptr new_chart;
// allocate and initialize new patient
new_chart = (Chartptr)malloc(sizeof(Chart));
new_chart->id = patientID;
new_chart->buffer = NULL;
// insert new patient into list
new_chart->next = patientList;
patientList = new_chart;
// test print patient data
printf("%d %d\n", new_chart->id, patientList->id);
}
}
/*
* getChart: given a patientID, return a pointer to their Chart
*/
Chartptr getChart(int patientID) {
Chartptr foundChart = NULL;
// find the patient chart with id
foundChart = patientList;
if (foundChart != NULL) {
while(foundChart->id != patientID) {
foundChart = foundChart->next;
}
}
return foundChart;
}
Here are the structs:
/*
* Patient's health chart: ID + linked list of health type readings
*/
typedef struct chartEntry* Chartptr; /* pointer to a Chart */
typedef struct chartEntry{
int id; /* patient ID */
CBuffptr buffer; /* pointer to first health type buffer */
Chartptr next; /* pointer to next patient */
}Chart;
extern Chartptr patientList; /* global declaration for start of the patient chart linked list */
I'm sending to the add patient, id, which I get from main, that much I know works perfectly.
But for some reason when the patientList is not NULL and it enters the while loop it seg faults or its after the while loop in the rest of addPatient. I don't know which. Thanks for your help.
I think here is your error:
while(foundChart->id != patientID) {
foundChart = foundChart->next;
You are updating foundChart but never checking in the while loop if it had become NULL, in case no patientID matches.
getChart() has no condition stop it running off the end of the list if no match is found.
Please update code as given below it will work :D
Chartptr getChart(int patientID) {
Chartptr foundChart = NULL;
// find the patient chart with id
foundChart = patientList;
while(foundChart!= NULL) {
if(foundChart->id == patientID) {
return foundChart;
}
foundChart = foundChart->next;
}
return NULL;
}