Structs in Linked List - c

I'm still fairly new to C programming and I'm having some trouble.
I've written some code to read a file that has information on people's names, ages, weight, and heights. Each new line in the file represents a new person. This information is then stored in a struct, and this struct is then added to a linked list. Essentially I am trying to use this linked list as a queue, with each node being a struct.
This is what the file I am reading will look like: (the format is age, weight, height, name).
20,60,170,Joe
23,70,175,Mike
My issue is that for some reason, every node in the linked list is the exact same, which is the last line of the file text. So for example every time I create a struct and add it to the linked list, each node will have the name 'Mike', age '23', and so forth. I've played around with the code and I notice that there is no issue with the file reading, and the values assigned to the struct members are correct (I printed out the values to check). However when I view the top of the Linked List, then dequeue (remove top), then view the top again, the values are always the same. So I am not sure where my problem is.
Note: I wrote a generic linked list code, and there is no issue with the linked list itself as I have tested it extensively.
This is my code:
#include <stdio.h>
#include "macros.h"
#include <string.h>
typedef struct
{
int AGE;
int WEIGHT;
int HEIGHT;
char NAME[51];
}Person;
int main()
{
void* vdPtr;
Person* topVal;
Person person;
LinkedList* list = createLinkedList();
char line[100];
int i=0;
int numPeople=0;
FILE* fp;
int age, weight, height;
char name[51];
fp = fopen("text.txt","r");
while(fgets(line,100,fp) != NULL)/*reading file to see how many lines, and how many people*/
{
numPeople++;
}
rewind(fp);
while (i<numPeople)/*storing file data in the appropriate member for Person struct*/
{
fscanf(fp,"%d,%d,%d,%s",&age,&weight,&height,name);
person.AGE = age;
person.WEIGHT = weight;
person.HEIGHT = height;
strcpy(person.NAME,name);
insertLast(list,&person);
/*printing to see whether the values added to struct members are correct*/
printf("%d %d %d %s\n",person.AGE,person.WEIGHT,person.HEIGHT,person.NAME);
i++;
}
/*testing*/
printf("\nCount %d",getLinkedListCount(list));/*seeing how many nodes in the linkedList*/
/*using void pointer to get the top value, then typecasting it to Person pointer*/
vdPtr = viewTop(list);
topVal = (Person*)(vdPtr);
/*viewing the top person*/
printf("\ntop is %s %d",topVal->NAME,topVal->AGE);
/*deqeue*/
removeTop(list);
printf("\nCount %d",getLinkedListCount(list));
vdPtr = viewTop(list);
topVal = (Person*)(vdPtr);
printf("\ntop is %s %d",topVal->NAME,topVal->AGE);
}
Can anyone tell me where I went wrong and what to do to fix it?

You are continually adding the same pointer to the list. On every new line you overwrite the same variable. So in the end you have one big list of pointers all pointing to the same thing. You have to make a new person struct for every person like this:
#include <stdio.h>
#include "macros.h"
#include <string.h>
typedef struct
{
int AGE;
int WEIGHT;
int HEIGHT;
char NAME[51];
}Person;
int main()
{
void* vdPtr;
Person* topVal;
LinkedList* list = createLinkedList();
char line[100];
int i=0;
int numPeople=0;
FILE* fp;
int age, weight, height;
char name[51];
fp = fopen("text.txt","r");
while(fgets(line,100,fp) != NULL)/*reading file to see how many lines, and how many people*/
{
numPeople++;
}
rewind(fp);
while (i<numPeople)/*storing file data in the appropriate member for Person struct*/
{
Person *person = (Person *)malloc(sizeof(Person)); //make new person object
fscanf(fp,"%d,%d,%d,%s",&age,&weight,&height,name);
person->AGE = age;
person->WEIGHT = weight;
person->HEIGHT = height;
strcpy(person->NAME, name);
insertLast(list, person);
/*printing to see whether the values added to struct members are correct*/
printf("%d %d %d %s\n",person.AGE,person.WEIGHT,person.HEIGHT,person.NAME);
i++;
}
/*testing*/
printf("\nCount %d",getLinkedListCount(list));/*seeing how many nodes in the linkedList*/
/*using void pointer to get the top value, then typecasting it to Person pointer*/
vdPtr = viewTop(list);
topVal = (Person*)(vdPtr);
/*viewing the top person*/
printf("\ntop is %s %d",topVal->NAME,topVal->AGE);
/*deqeue*/
removeTop(list);
printf("\nCount %d",getLinkedListCount(list));
vdPtr = viewTop(list);
topVal = (Person*)(vdPtr);
printf("\ntop is %s %d",topVal->NAME,topVal->AGE);
}
I recommend reading up on how pointers, and memory management work. Also keep in mind that this code does not free the memory it allocates, which is something you still need to do.

Without seeing code for insertLast() it is difficult too answer but it may be that you would create a new struct for each person and then pass it to insertLast :
Person* newPerson;
while (i<numPeople)/*storing file data in the appropriate member for Person struct*/
{
newPerson = (Person *)malloc(sizeof(Person))
fscanf(fp,"%d,%d,%d,%s",&age,&weight,&height,name);
newPerson->AGE = age;
newPerson->WEIGHT = weight;
newPerson->HEIGHT = height;
strcpy(newPerson.NAME,name);
insertLast(list,newPerson);
...
The list seems to save only the address of the data, that why you need to create new data for each person.

A struct that is to be used in a linked list needs to be self referential. i.e. one of the members (typically the last one) is a pointer to the struct itself. This is what allows segments of data to be linked together . (other members can be thought of as the payload, or data.) For example, your struct:
typedef struct
{
int AGE;
int WEIGHT;
int HEIGHT;
char NAME[51];
}Person;
Can be refactored for example as:
struct Person
{
int AGE;
int WEIGHT;
int HEIGHT;
char NAME[51];
struct Person *node;
};
Look at this simple starter tutorial for creating and using linked lists. (Click the C tab for example code.)

Related

C is not printing my valid variable value

I have a new typedef struct and an init function for it.
#include <stdlib.h>
#include <stdio.h>
typedef struct PERSON
{
char * name;
char * surname;
int month;
int day;
} * Person;
Person init_person(char * name, char * surname, int month, int day)
{
Person person = calloc(1, sizeof(Person));
person->name = name;
person->surname = surname;
person->month = month;
person->day = day;
return person;
}
int main(int argc, char * argv[])
{
Person person = init_person("Hello", "World", 12, 12);
printf("\n%s\n", person->name);
printf("\n %i %i \n", person->month, person->day);
return 0;
}
When the code is run, the expected output should be:
Hello
12 12
But instead, I am getting:
Hello
0 1769108595
When I swap around the printf function statements, I do get the desired output. I have written similar code using structures before but I have not faced such a problem. Could it be a memory problem?
See Is it a good idea to typedef pointers? TL;DR — No, except perhaps for function pointers or pointers to opaque types, neither of which is relevant here.
And the problem is that you've been suckered into allocating insufficient memory by the pointer typedef. You have:
Person person = calloc(1, sizeof(Person));
That allocates enough space for one pointer to a struct PERSON. You need to use something equivalent to:
Person person = calloc(1, sizeof(*person));
to allocate enough space for what person will point to.
Or you need to remove the pointer from the typedef and make corresponding changes to the code. That will be better in the long run.
You don't want to declare your structure as a pointer,
typedef struct PERSON
{
char * name;
char * surname;
int month;
int day;
} Person;
And if you need to make it a pointer,
Person *person = (Person *) malloc(sizeof(Person));

How can I build a function that stores data in a structure when taken from a file (pointer)?

In an assignment I'm working on, I need to read data from a file and then convert that data into a form that can be stored in a structure. The structure stores information on the person the line in the file represents. The issue that I'm having is that I cannot find a way to build a function that will allow me to take this data and store it in the structure. This is the relevant code to getting the file:
FILE* ifp;
char file_name[150], early_type[5], name_holder[LENGTH];
double early_registration, indi_registration, team_registration, amount_holder;
int num_early_regist, age_holder, type_holder;
// get file name and set it up as a pointer
printf("Please enter the name of your file.\n");
scanf("%s", file_name);
ifp = fopen(file_name, "r");
The code then goes to scanning the file for the data:
fscanf(ifp, "%s %s %d %d %lf", early_type, name_holder, &age_holder, &type_holder, &amount_holder);
// if the line shows an individual registering early
if (strcmp(early_type, "INDV")==0){
person_early(struct person* person, &name_holder, &age_holder, &type_holder, &amount_holder);
}
Finally, this is the prototype of the function that I am building:
void person_early(struct person* person, char* name[LENGTH], int* age, int* event_type, double* donation_amount);
The issue that I'm having is that I'm not sure how to put the data into this structure:
struct person {
char name[LENGTH];
int number;
int age;
int event;
float money;
float time;
};
Should I build a function to store this data, as there are several people to log from the file? Do I need to be using pass by value or pass by reference? Thanks for any help.
Edit:
The full function that I have written is this:
void person_early(struct person* person, char* name[LENGTH], int* age, int* event_type, double* donation_amount) {
// the name of the variable should be the name that is taken from the file
struct person (*name[LENGTH]);
(*name).name = *name_holder;
(*name).age = *age_holder;
(*name).event = *type_holder;
(*name).money = *donation_amount;
return;
}
regarding:
(*name).name = *name_holder;
This is not what you want. The passed in struct pointer person is the receiver of all the data. suggest:
void person_early( struct person* person, char name[], int age, int event_type, double donation_amount)
{
strcpy( person->name, name );
person->age = age;
person->event = event_type;
person->money = donation_amount;
}
Note: no return needed on a void function, unless exiting before the end of the function

Putting Linked List inside structure in C (segmentation fault)

I have a task to make something that looks like Hotel Booking program in C.
Basically I should have 2 structures, one for reservations of rooms in the hotel (that contains information about that room like its ID, floor, how many beds are there and stuff like that, pointer to person that reserved the room and list of people that will be in that room) and the other structure for persons (which contains, lets say, name and surname of person).
The problem I am having is segmentation fault that I get since I probably didn't make those structures right.
Here is a part of my code, that is supposed to add a guest inside the list (that is located in reservation structure):
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#define MAXSTR 25
#define FORMAT_LEN 15
typedef struct person //structure that holds info about person
{
char name[MAXSTR + 1];
char surname[MAXSTR + 1];
}Guest;
typedef struct persons // list of persons
{
Guest person;
struct persons* next;
} Persons;
typedef struct //Pointer to first person in list
{
Persons* list;
} List;
typedef struct room //structure that holds info about rooms that are reserved
{
unsigned int ID;
unsigned int floor;
unsigned int bedcount;
struct tm dateArrival;
struct tm dateDep;
bool cancelled;
unsigned int price;
Guest* person;
List* list;
}Reservation;
typedef struct rooms
{
Reservation room;
struct rooms* next;
} Reservations;
bool PutGuestInsideList(List* list);
char* EnterString(char* str);
int main()
{
Reservations* room = NULL;
List list = { NULL };
printf("New guest: \n");
if(PutGuestInsideList(room->room.list) == false)
printf("Try again. \n");
}
bool PutGuestInsideList(List* list)
{
if(list == NULL)
return false;
Persons* newpers = (Persons*) malloc(sizeof(Persons));
if(newpers == NULL)
return false;
printf("Name: ");
EnterString(newpers->person.name);
printf("Surname: ");
EnterString(newpers->person.surname);
list->list = newpers;
return true;
}
char* EnterString(char* str)
{
if (str == NULL)
return str;
char format[FORMAT_LEN] = "";
sprintf(format, "%%%u[^\n]", MAXSTR);
scanf(format, str);
scanf("%*[^\n]");
scanf("%*c");
return str;
}
If there is something I left out in my explanation ask me in the comments. Thanks!
In main :
int main()
{
Reservations* room = NULL;//here you initialise pointer with NULL
List list = { NULL };
printf("New guest: \n");
if(PutGuestInsideList(room->room.list) == false)// and here you try to access it
printf("Try again. \n");
}
At if statement you are accessing member room.list on pointer room even if room is NULL.
BTW: Use (or learn to use) debugger, you will be able to debug your programs faster.(that's why they are called debuggers)
You dereferenced room which is NULL in the line if(PutGuestInsideList(room->room.list) == false). You can not dereference a NULL pointer. The room pointer should be properly set in your main function.
I think you need to spend some time on google looking up tutorials on pointers in C. As others are pointing out, room is set to null but you dereference it.
Reservations* room = NULL;
// and then
if(PutGuestInsideList(room->room.list) == false)
you also need to review these lines:
EnterString(newpers->person.name);
EnterString(newpers->person.surname);
Neither person.name nor person.name is given a buffer to write in. They are pointers which do not point anywhere but you attempt to use them.

Segmentation fault when I try to printf

Why am I getting a segmentationfault when it tries to print the second member in the list?
After printing the first element of the list, the debugger opens the stdio.h and says:
At C:\TDM-GCC-32\include\stdio.h:255
At C:\TDM-GCC-32\include\stdio.h:256
At C:\TDM-GCC-32\include\stdio.h:258
At C:\TDM-GCC-32\include\stdio.h:259
Here is the code.
#include <stdio.h>
#include <stdlib.h>
struct Student {
char *Name;
char *Adresse;
unsigned long Mtnr;
short Kurse;
struct Student *next;
struct Student *previous;
};
typedef struct Student Student;
Student *liste = NULL, *ende = NULL;
void add(char Name, char Adresse, unsigned long Mtnr, short Kurse) {
Student *add;
ende->next = malloc(sizeof(Student));
add = ende->next;
add->Name = Name;
add->Adresse = Adresse;
add->Mtnr = Mtnr;
add->Kurse = Kurse;
add->previous = ende;
add->next = NULL;
ende = ende->next;
}
void Ausgabe(Student *Anfang) {
while (Anfang != NULL) {
printf("%s %s %d %d \n", Anfang->Name, Anfang->Adresse, Anfang->Mtnr, Anfang->Kurse);
Anfang = Anfang->next;
}
}
int main() {
liste = malloc(sizeof(Student));
ende = liste;
liste->Name = "Anna Musterfrau";
liste->Adresse = "Am Schwarzberg-Campus 3";
liste->Mtnr = 22222;
liste->Kurse = 2;
liste->next = NULL;
liste->previous = NULL;
add("Hans Peter", "Kasernenstrasse 4", 4444, 4);
Ausgabe(liste);
return 0;
}
The error is in the declaration of the add() function. The strings should be char pointers, not chars.
void add(char *Name, char *Adresse, unsigned long Mtnr, short Kurse){
The signature of the function add is inconsistent to the declaration and usage to the members of Student. Change the signature as follows.
void add(char* Name, char* Adresse, unsigned long Mtnr, short Kurse)
On the long run, it might also be necessary to create copies of Name and Adresse in add, as the caller of add might deallocate them, perhaps causing undesired behaviour.
While Marc Is correct with his observation, there is one more thing you may want to fix here.
When you add a record, you allocate it, but you do not allocate the content of it's pointers (Specifically = for the name and address pointers). The add function just point them to the input's address. this is a problem because the data in the address supplied to the add function is likely to change if, for example, it's a user input, or some other external buffer.
in the code snipped below I fixed the 'name', but left the address as is. please run it and see what happens. (assaf's record displays david's address)
hope this helps
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student {
char *Name;
char *Adresse;
unsigned long Mtnr;
short Kurse;
struct Student *next;
struct Student *previous;
};
typedef struct Student Student;
Student *liste = NULL, *ende = NULL;
void add(char *Name, char *Adresse, unsigned long Mtnr, short Kurse) {
Student *add;
ende->next = malloc(sizeof(Student));
add = ende->next;
add->Name = malloc(strlen(Name)+1);
strcpy(add->Name, Name);
add->Adresse = Adresse;
add->Mtnr = Mtnr;
add->Kurse = Kurse;
add->previous = ende;
add->next = NULL;
ende = ende->next;
}
void Ausgabe(Student *Anfang) {
while (Anfang != NULL) {
printf("%s %s %d %d \n", Anfang->Name, Anfang->Adresse, Anfang->Mtnr, Anfang->Kurse);
Anfang = Anfang->next;
}
}
int main() {
char name_buf[100];
char address_buf[100];
liste = malloc(sizeof(Student));
ende = liste;
liste->Name = "Anna Musterfrau";
liste->Adresse = "Am Schwarzberg-Campus 3";
liste->Mtnr = 22222;
liste->Kurse = 2;
liste->next = NULL;
liste->previous = NULL;
add("Hans Peter", "Kasernenstrasse 4", 4444, 4);
sprintf(name_buf,"assaf stoler");
sprintf(address_buf,"maria 8");
add(name_buf, address_buf, 8888, 8);
sprintf(name_buf,"david david");
sprintf(address_buf,"some street 9");
add(name_buf, address_buf, 9999, 9);
Ausgabe(liste);
return 0;
}
EDIT: Op asked some questions, and the comment space is limited, so I'll add below:
A pointer is just an object pointing somewhere in memory. it's size is fixed. the content it's pointing to will vary.
When you include a pointer to a string in a structure, the space where the string is kept need to be allocated / accounted for. it is not part of the sizeof(struct).
In your original example the pointers were pointing to constant strings (which reside in the static code, usually the data section, allocated by the compiler), which is why your original code was able to access the strings.
In a more realistic case, the input data is not part of the program data, but is received by some input method (which my *buf was to emulate). as such, pointing your name and address at it would break the program, as the pointer you point to may have it's content changed. therefor copying of the data (string / array) is needed, and since we copy the data, we need to allocate space for it, and point our (name/address) pointer at it.
Alternate option is to use non-pointer array for name and address as in:
struct Student {
char Name[20];
char Adresse[60];
unsigned long Mtnr;
...
}
In this scenario, sizeof (struct Student) would actually include all the space for those fields. you still need to use strcpy or memcpy, as well as check for and handle strings that are too long to fit in your pre-defined length.
Hope this helps

C struct array and search: Attempting to a match a member and prnt the entire struct if a match is found.

here is the struct
int main() {
typedef struct {
char firstName[25];
char lastName[30];
char street[35];
char city[20];
char state[3];
int zip;
char phone[15];
int accountId;
}Customer ;
say i fill this out with x amount of data.
what is a simple way to search the array index of this struct based on one its members, and then print that "Customers" info. Specifically I am looking to search for customers by state.
Below is an example that I believe will be of some help. Of course, the Customer definition, record printing, and data population need to be expanded. Also note that customer[] is on the stack in this example, so its members aren't zeroed and hence should be set to intended values one way or another.
#include <string.h>
#include <stdio.h>
#define NUM_RECORDS 10
int main()
{
int i;
typedef struct {
char state[3];
} Customer;
Customer customer[NUM_RECORDS];
strcpy(customer[2].state, "CA");
for (i = 0; i < NUM_RECORDS; i++)
{
// if this customer record's state member is "CA"
if (!strcmp(customer[i].state, "CA"))
printf("state %d: %s\n", i, customer[i].state);
}
// Prints "state 2: CA"
return 0;
}

Resources