Can you help me understand why I am getting these segmentation faults? - c

I am trying to build this little database of students, using linked lists in C. For some reason I am getting a segmentation fault the second time I run this function. I have a switch inside a while loop where I can run the different functions by entering a number from the keyboard. So when I start the program, press 1 to run this function below, everything works fine. But when I try to run it a second time without terminating the program in between, it crashes and the debugger tells me that there was a segmentation fault. I can't figure out why.
This is a function in my script.
int add_student(void){
current_student = student_head;
current_study = study_head;
if (current_study == NULL || current_study == NULL)
return 1;
while(current_student->next_student != NULL){
current_student = current_student->next_student;
}
current_student->next_student = (student*)malloc(sizeof(student));
printf("Enter the persons personal number.");
scanf("%d", &current_student->next_student->pernum);
printf("Enter the persons name");
scanf("%s", current_student->next_student->name);
printf("Male or female? Write 1 for male and 0 for female.");
scanf("%d", &current_student->next_student->is_male);
if(current_student->next_student->is_male == 0)
num_of_female++;
else
num_of_male++;
printf("Enter the persons age");
scanf("%d", &current_student->next_student->age);
printf("Enter the persons email");
scanf("%s", current_student->next_student->email);
while(current_study->next_study != NULL){
current_study = current_study->next_study;
}
current_study->next_study = (study*)malloc(sizeof(study));
current_study->next_study->pernum = current_student->next_student->pernum;
printf("Does this student study math? Asnwer 1 for yes or 0 for no."); // Field of study.
scanf("%d", &current_study->next_study->math);
printf("Does this student study english? Asnwer 1 for yes or 0 for no.");
scanf("%d", &current_study->next_study->english);
printf("Does this student study chemistry? Asnwer 1 for yes or 0 for no.");
scanf("%d", &current_study->next_study->chemistry);
printf("Does this student study biology? Asnwer 1 for yes or 0 for no.");
scanf("%d", &current_study->next_study->biology);
printf("Does this student study history? Asnwer 1 for yes or 0 for no.");
scanf("%d", &current_study->next_study->history);
num_of_stud++;
}
I just want the function to be able to run an indefinite amount of times without crashing.
When I run the function the first time, current_student has a memory address as expected and current_student->next_student is NULL as expected. The second time current student has the same address and current_student->next_student has an address, so everything seems to be working so far?
Current_student, student head, current_study and study_head are all declared as global pointers.
Edit: Here are the declarations to the structs and the pointers to allocated memory. These are outside main on the top level outside any function:
typedef struct student{
int pernum;
char name[100];
int is_male;
int age;
char email[100];
struct student *next_student;
}student;
typedef struct study{
int pernum;
int math;
int english;
int chemistry;
int biology;
int history;
struct study *next_study;
}study;
int num_of_stud = 0;
int num_of_female = 0;
int num_of_male = 0;
student *current_student;
study *current_study;
student *student_head = NULL;
study *study_head = NULL;

Without seeing the content of your main function and how the add_student function was being called, I took a bit of artistic license to refactor some of the function so that it would sense when the first student structure or study structure was being added and act accordingly to provide a proper starting point and subsequent linked list chain. Following is a version of your function with some tweaks to determine a starting student/study point along with a main function that allows for a continuous number of student and study records to be entered. It is a bit lengthy, but I could not figure how to condense this any further.
#include <stdio.h>
#include <stdlib.h>
typedef struct student
{
int pernum;
char name[100];
int is_male;
int age;
char email[100];
struct student *next_student;
} student;
typedef struct study
{
int pernum;
int math;
int english;
int chemistry;
int biology;
int history;
struct study *next_study;
} study;
int num_of_stud = 0;
int num_of_female = 0;
int num_of_male = 0;
student *current_student = NULL;
study *current_study = NULL;
student *student_head = NULL;
study *study_head = NULL;
int add_student(void)
{
int resp = 0; /* Added to provide a user controlled end to student and study data entry */
if (current_student == NULL) /* If this is the initial student set up the initial structure allocation */
{
current_student = malloc(sizeof(student));
student_head = current_student;
}
else /* Else, chase down the linked list to the last current student */
{
while(current_student->next_student != NULL)
{
current_student = current_student->next_student;
}
}
current_student->next_student = malloc(sizeof(student));
printf("Enter the person's personal number: ");
scanf("%d", &current_student->next_student->pernum);
printf("Enter the persons name: ");
scanf("%s", current_student->next_student->name);
printf("Male or female? Write 1 for male and 0 for female: ");
scanf("%d", &current_student->next_student->is_male);
if(current_student->next_student->is_male == 0)
num_of_female++;
else
num_of_male++;
printf("Enter the persons age ");
scanf("%d", &current_student->next_student->age);
printf("Enter the persons email ");
scanf("%s", current_student->next_student->email);
current_student->next_student->next_student = NULL; /* Make sure the last student element has its pointer properly initialized */
if (current_study == NULL) /* If this is the initial study element set up the initial structure allocation */
{
current_study = malloc(sizeof(study));
study_head = current_study;
}
else /* Else, chase down the linked list to the last current study element */
{
while(current_study->next_study != NULL)
{
current_study = current_study->next_study;
}
}
current_study->next_study = malloc(sizeof(study));
current_study->next_study->pernum = current_student->next_student->pernum;
printf("Does this student study math? Asnwer 1 for yes or 0 for no: "); // Field of study.
scanf("%d", &current_study->next_study->math);
printf("Does this student study english? Asnwer 1 for yes or 0 for no: ");
scanf("%d", &current_study->next_study->english);
printf("Does this student study chemistry? Asnwer 1 for yes or 0 for no: ");
scanf("%d", &current_study->next_study->chemistry);
printf("Does this student study biology? Asnwer 1 for yes or 0 for no: ");
scanf("%d", &current_study->next_study->biology);
printf("Does this student study history? Asnwer 1 for yes or 0 for no: ");
scanf("%d", &current_study->next_study->history);
current_study->next_study->next_study = NULL; /* Make sure the last study element has its pointer properly initialized */
num_of_stud++;
printf("More students 0/1 "); /* Send a response back to the main function to determine if further entry will occur */
scanf("%d", &resp);
if (resp == 0)
{
return -1;
}
return 0;
}
int main()
{
while (1) /* A test harness to allow a variable number of students to be entered */
{
if (add_student() == -1)
{
break;
}
}
current_student = student_head->next_student; /* This bit added just to verify what was entered */
printf("Student list\n-----------------------------------------\n");
while (1)
{
printf("Number: %d Name: %s\n", current_student->pernum, current_student->name);
if (current_student->next_student == NULL)
{
break;
}
current_student = current_student->next_student;
}
current_student = student_head->next_student;
printf("\nStudy list\n-----------------------------------------\n");
while (1)
{
printf("Person number: %d Math: %d English: %d Chemistry: %d Biology: %d History: %d\n", current_study->pernum, current_study->math, current_study->english, current_study->chemistry, current_study->biology, current_study->history);
if (current_study->next_study == NULL)
{
break;
}
current_study = current_study->next_study;
}
return 0;
}
Some points to note.
Instead of immediately testing the pointer value in the student_head and study_head structure and exiting, that information is used to determine if the call to this function is meant to set up the initial student and study structures in the linked list.
As noted in the comments, the casting of the malloc function was removed.
To insure the linked list pointer integrity, the next student and next study pointers are set to NULL for every newly created student and study structure.
A response variable was added to provide a very simple method for indicating that student and study data entry is done; something more sophisticated might be used to determine when data entry is complete.
Just as a proof of function, the entered student and study records are printed out in the main function.
Following is some sample terminal output for two student/study entries; however, more students and their associated study structures could have been entered.
#Vera:~/C_Programs/Console/StudentBody/bin/Release$ ./StudentBody
Enter the person's personal number: 22
Enter the persons name: Craig
Male or female? Write 1 for male and 0 for female: 1
Enter the persons age 18
Enter the persons email craig#somemail.com
Does this student study math? Asnwer 1 for yes or 0 for no: 1
Does this student study english? Asnwer 1 for yes or 0 for no: 0
Does this student study chemistry? Asnwer 1 for yes or 0 for no: 0
Does this student study biology? Asnwer 1 for yes or 0 for no: 0
Does this student study history? Asnwer 1 for yes or 0 for no: 0
More students 0/1 1
Enter the person's personal number: 33
Enter the persons name: Lily
Male or female? Write 1 for male and 0 for female: 0
Enter the persons age 19
Enter the persons email lily#somemail.com
Does this student study math? Asnwer 1 for yes or 0 for no: 0
Does this student study english? Asnwer 1 for yes or 0 for no: 1
Does this student study chemistry? Asnwer 1 for yes or 0 for no: 0
Does this student study biology? Asnwer 1 for yes or 0 for no: 0
Does this student study history? Asnwer 1 for yes or 0 for no: 0
More students 0/1 0
Student list
-----------------------------------------
Number: 22 Name: Craig
Number: 33 Name: Lily
Study list
-----------------------------------------
Person number: 22 Math: 1 English: 0 Chemistry: 0 Biology: 0 History: 0
Person number: 33 Math: 0 English: 1 Chemistry: 0 Biology: 0 History: 0
Also, you may want to explore passing your structure or structure address as function parameters in lieu of using global variables as noted in the comments. But in order to provide a timely response with the code as is, I left your variables as global variables.
Give that a try and see if it meets the spirit of your project.

Related

Finding three leaders from arrays

Thank you for visiting this question. I know this looks like a question from a book which it totally is. I couldn't find the solution for this anywhere and I cant get one thing. Supposedly the code compare things like studs[i].score within for loop, but why it can assign the value of studs[i].score to another element of the struct like say first.score? The same goes for studs[i].name = first.name the program wont even compile. Any input matter, have been sitting with this for a week.
Have a great day!
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct stud {
char name[50];
float score;
};
//Please do not modify struct stud
//You are only allowed to modify inside printThreeLeaders
void printThreeLeaders(struct stud studs[], int count) { //why is count here? C arrays do not carry any size indicator, we
//must explicitly pass the number of elements in as an argument
//Please do not modify the content of studs array
struct stud first, second, third;
//Your code here
for (int i=0;i<count;i++){
if (studs[i].score>third.score){
if(studs[i].score>second.score){
if (studs[i].score>first.score){
studs[i].score=first.score;
studs[i].name=first.name;
}
}studs[i].score=second.score;
studs[i].name=second.name;
}studs[i].score=third.score;
studs[i].name=third.name;
}
//Please find the top three highest scoring students on the leaderboard
//and print out their names and scores.
//You are allowed to use string functions such as strcmp or strcpy
//Although you might not need them
//Please do not modify the following code
printf("Leader board:\n");
printf("First place: %s, %.2f\n", first.name, first.score);
printf("Second place: %s, %.2f\n", second.name, second.score);
printf("Third place: %s, %.2f\n", third.name, third.score);
}
//Please do not modify main function
int main(void) {
struct stud students[20];
int stud_count = 0;
char temp_name[50];
float grade = 0;
printf("Enter a test score(-1 to quit), or\n");
printf("Enter a grade first, then a student's name\n");
scanf("%f", &grade);
while (grade != -1)
{
scanf("%s", temp_name);
students[stud_count].score = grade;
strcpy(students[stud_count].name, temp_name);
stud_count ++;
printf("Enter a test score(-1 to quit), or\n");
printf("Enter a grade first, then a student's name\n");
scanf("%f", &grade);
}
if(stud_count > 2) {
printThreeLeaders(students, stud_count);
}
return 0;
}
A few issues:
You do modify the studs array with: studs[i].score=second.score;
The three variables first, second, and third are uninitialized so you have UB (undefined behavior)
You don't need to use str* functions to copy the name if you copy the whole struct.
Here is the refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stud {
char name[50];
float score;
};
// Please do not modify struct stud
// You are only allowed to modify inside printThreeLeaders
void
printThreeLeaders(struct stud studs[], int count)
{
// why is count here? C arrays do not carry any size indicator, we
// must explicitly pass the number of elements in as an argument
// Please do not modify the content of studs array
// NOTE/BUG: first/second/third are _not_ initialized
#if 0
struct stud first, second, third;
#else
struct stud first = { .score = -1 };
struct stud second = { .score = -1 };
struct stud third = { .score = -1 };
#endif
// Your code here
for (int i = 0; i < count; i++) {
const struct stud *st = &studs[i];
float score = st->score;
if (score > first.score) {
third = second;
second = first;
first = *st;
continue;
}
if (score > second.score) {
third = second;
second = *st;
continue;
}
if (score > third.score) {
third = *st;
continue;
}
}
// Please find the top three highest scoring students on the leaderboard
// and print out their names and scores.
// You are allowed to use string functions such as strcmp or strcpy
// Although you might not need them
// Please do not modify the following code
printf("Leader board:\n");
printf("First place: %s, %.2f\n", first.name, first.score);
printf("Second place: %s, %.2f\n", second.name, second.score);
printf("Third place: %s, %.2f\n", third.name, third.score);
}
// Please do not modify main function
int
main(void)
{
struct stud students[20];
int stud_count = 0;
char temp_name[50];
float grade = 0;
printf("Enter a test score(-1 to quit), or\n");
printf("Enter a grade first, then a student's name\n");
scanf("%f", &grade);
while (grade != -1) {
scanf("%s", temp_name);
students[stud_count].score = grade;
strcpy(students[stud_count].name, temp_name);
stud_count++;
printf("Enter a test score(-1 to quit), or\n");
printf("Enter a grade first, then a student's name\n");
scanf("%f", &grade);
}
if (stud_count > 2) {
printThreeLeaders(students, stud_count);
}
return 0;
}
In the above code, I've used cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Note: this can be cleaned up by running the file through unifdef -k
Here is the test input I used:
1 Fred
2 Bob
3 Alice
4 John
5 Mary
6 Frank
7 Abel
8 Cain
9 Peter
10 Kilroy
11 Smith
12 Jones
-1
Here is the [cleaned up] program output:
Leader board:
First place: Jones, 12.00
Second place: Smith, 11.00
Third place: Kilroy, 10.00
UPDATE:
Amazing, it does work. The problem was in initialization of first, second and third. With this edits it does work. My incorrect reasoning was that initialization happened at 'struct stud first second third'. –
JEDi455
C is all about minimalism and speed.
Initialization [of stack based variables] is not done by default for speed.
Here, for this problem, explicit initialization was needed.
But, in another problem, suppose we had (e.g.):
int x,y,z;
If we explicitly assign them values with:
/* some small blob of code unrelated to x/y/z ... */
x = funcA();
y = funcB(x);
z = funcC(x,y);
Then, we'd be cursing the compiler for wasting time by initializing them to default values, only to overwrite those values with our explicit code.
That is, if the compiler always treated:
int x,y,z;
as:
int x = 0, y = 0, z = 0;
We'd not want the compiler to "help" us in this way. That is, if we wanted the latter, we'd have written that.
C gives the programmers full control [and assumes they know what they're doing]. The compiler will try to help by flagging statements with errors or warnings, but it's often up to the programmer.

printing wrong number from defined function related to struct in c

I had an assignment about entering an unknown number of the students' information and then printing them out. the code that I wrote works properly for one student but when I enter other students, only the output of the phone numbers and IDs are wrong but there isn't any error message. This is the shorter version of my code. I was hoping that someone could help me find the error.
Thank you for helping in advance!!
code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STRSIZE 75
struct student GetData();
int print_info (struct student *STU, int i);
struct company
{
char Co_Intern[STRSIZE];
long int Co_Tel; ///this variable has wrong output
};
struct student
{
long int ID; ///this variable has wrong output
char name[STRSIZE];
long int Tel; ///this variable has wrong output
struct company COE;
int Tot_days;
struct Date
{
int dd;
int mm;
int yyyy;
}doj;
};
int main()
{
struct student *STU;
int i, n;
printf("Enter the number of students: ");
scanf("%d", &n);
STU = (struct student*) malloc(n * sizeof(struct student));
for(i = 0; i < n; ++i)
{
STU[i]= GetData(); //calling a struct function to scan data
}
printf("\nDisplaying Information:\n==============================\n\n");
for(i = 0; i < n; ++i)
{
print_info (STU, i); //calling a function to print data
}
return 0;
}
struct student GetData() ///struct function to scan data
{
struct student info_s;
printf("\nEnter student's information:\n");
printf("Student ID:\t");
scanf("%li", &info_s.ID);
printf("Student's name:\t");
getchar();
gets(info_s.name);
printf("Student Telephone number:\t");
scanf("%li", &info_s.Tel);
printf("\nEnter student's internship company date and information:\n");
printf("Student's Company of internship:\t");
scanf("%s", info_s.COE.Co_Intern);
printf("Company's Telephone:\t");
scanf("%li", &info_s.COE.Co_Tel);
printf("Beginning date: dd-mm-yyyy:\t");
scanf("%d%d%d", &info_s.doj.dd, &info_s.doj.mm, &info_s.doj.yyyy);
printf("Total days of internship:\t");
scanf("%d", &info_s.Tot_days);
return(info_s);
}
int print_info (struct student *STU, int i)
{
printf("\nStudent-%d:\t\t\t\t\t\t",i+1);
printf("\n\nStudent ID:\t\t\t\t\t%li", STU[i].ID);
printf("\n\nStudent's name:\t\t\t\t\t%s", STU[i].name);
printf("\n\nStudent Telephone number:\t\t\t%li", STU[i].Tel);
//here is the problem
printf("\n\nStudent's Company of internship:\t\t%s", STU[i].COE.Co_Intern);
printf("\n\nCompany's Telephone:\t\t\t\t%li", STU[i].COE.Co_Tel);
//here is the problem
printf("\n\nBeginning date: dd-mm-yyyy:\t\t\t%d%d%d", STU[i].doj.dd, STU[i].doj.mm, STU[i].doj.yyyy);
printf("\n\nTotal days of internship:\t\t\t%d", STU[i].Tot_days);
printf("\n\n==============================\n\n");
}
and this is the output in which the long numbers are wrong:
Enter the number of students: 2
Enter student's information:
Student ID: 1234567890
Student's name: xxxx xxxx
Student Telephone number: 987654321
Enter student's internship company date and information:
Student's Company of internship: aaaa
Company's Telephone: 5554443330
Beginning date: dd-mm-yyyy: 22-10-2020
Total days of internship: 365
Enter student's information:
Student ID: 1112223330
Student's name: nnnn ssss
Student Telephone number: 9998887770
Enter student's internship company date and information:
Student's Company of internship: name
Company's Telephone: 3333344444
Beginning date: dd-mm-yyyy: 20-10-2020
Total days of internship: 365
Displaying Information:
==============================
Student-1:
Student ID: 1234567890
Student's name: xxxx xxxx
Student Telephone number: 987654321
Student's Company of internship: aaaa
Company's Telephone: 1259476034
Beginning date: dd-mm-yyyy: 22-10-2020
Total days of internship: 365
==============================
Student-2:
Student ID: 1112223330
Student's name: nnnn ssss
Student Telephone number: 1408953178
Student's Company of internship: name
Company's Telephone: -961622852
Beginning date: dd-mm-yyyy: 20-10-2020
Total days of internship: 365
==============================
as you can see the following lines have problem, and only the IDs were correct however sometimes randomly the second Id is also wrong and has the wrong value but the dates and durations that I have in the code are correct. i also tried other ways that others asked about like scanning them individually as an int and then assigning them to their related struct but it didn't work as well.
Company's Telephone: -961622852
Student Telephone number: 1408953178
Company's Telephone: 1259476034
There are probably other issues, but the input string "%d%d%d" does not match the input given for the date. As a result, the scanf treats the values as negative numbers. To avoid this error, perhaps use a different input format. To avoid this type of error in general, you must always check the value returned by scanf, every time you call scanf. For example, perhaps use dd/mm/yy with:
if( 3 != scanf("%d/%d/%d", &info_s.doj.dd, &info_s.doj.mm, &info_s.doj.yyyy) ){
fprintf(stderr, "Invalid input\n");
exit(EXIT_FAILURE);
}
If you want to provide a friendlier interactive interface (which is best accomplished by using a language other than C!), you should probably not use scanf to read the input. Instead, use fgets and then parse the data (possibly with sscanf, but really you're better off avoiding the entire scanf family) after it has been read. Attempting to recover from unexpected input using scanf is often more difficult than it is worth.
See: http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html

Having trouble printing out my structure array in c

My current project in class is to make a program that acts as a phone book using structs. I have coded out the 'add contacts' function and the 'show contacts' function, but when I have more than one contact, the 'show contacts' function shows gibberish for all of the contacts expect for the most recent one. Can anybody help me find the problem?
Here is my current code. If anything is confusing let me know and I will try to respond as fast as I can.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct contact { // Data structure that holds contact information
char FirstName[10]; // Array for first name
char LastName[10]; // Array for last name
int PhoneNum; // Phone number
};
int main (){
// Setting up variables
int Function = 0;
int Choice = 0;
char FName[200][10];
char LName[200][10];
int PNum = 0;
int n = 1;
struct contact *con[200];
int b = 0;
do{ // Will loop through the main function until the user decides to exit the program
// Prints out the main menu of the phone book
printf("\nPhone Book");
printf("\n[1] Add a contact");
printf("\n[2] Delete a contact");
printf("\n[3] Show contacts");
printf("\n[4] Exit program");
printf("\n\nWhat function would you like to use?\n"); // Asks for user input
scanf("%d", &Choice);
switch(Choice){
case 1: // Case to add a contact into the phone book
printf("\nYou chose to add a contact.");
printf("\nFirst name: ");
scanf("%s", &FName[b]);
printf("\nLast name: ");
scanf("%s", &LName[b]);
printf("\nPhone number (Numbers only): ");
scanf("%d", &PNum);
printf("\nRecord added to the phone book");
// Records the information given into the structure
struct contact *con = (struct contact*)malloc(n*sizeof(struct contact));
int a = (n - 1);
printf("\na is %d", a);
strcpy(con[a].FirstName, FName[b]);
strcpy(con[a].LastName, LName[b]);
con[a].PhoneNum = PNum;
int b = (b+1);
n++;
printf("\nn is %d", n);
// Prints out the given information
printf("\nNew contact:");
printf("\nFirst name: %s", con[a].FirstName);
printf("\nLast name: %s", con[a].LastName);
printf("\nPhone number: %d", con[a].PhoneNum);
printf("\n");
break;
case 2: // Case to delete a contact from the phone book
printf("\nYou chose to delete a contact.");
break;
case 3: // Case to see all of the entered contacts
printf("\nYou chose to show the contacts.");
for(a = 0; a < (n - 1); a++){
printf("\nContact #%d", a);
printf("\nFirst name: %s", con[a].FirstName);
printf("\nLast name: %s", con[a].LastName);
printf("\nPhone number: %d", con[a].PhoneNum);
printf("\n");
}
break;
case 4:
printf("Goodbye!");
break;
}
}while (Choice != 4);
return 0;
}
This is the output I get when I run my code.
Phone Book
[1] Add a contact
[2] Delete a contact
[3] Show contacts
[4] Exit program
What function would you like to use?
1
You chose to add a contact.
First name: Test
Last name: 1
Phone number (Numbers only): 1234567
Record added to the phone book
a is 0
n is 2
New contact:
First name: Test
Last name: 1
Phone number: 1234567
Phone Book
[1] Add a contact
[2] Delete a contact
[3] Show contacts
[4] Exit program
What function would you like to use?
1
You chose to add a contact.
First name: test
Last name: 2
Phone number (Numbers only): 8901234
Record added to the phone book
a is 1
n is 3
New contact:
First name: test
Last name: 2
Phone number: 8901234
Phone Book
[1] Add a contact
[2] Delete a contact
[3] Show contacts
[4] Exit program
What function would you like to use?
3
You chose to show the contacts.
Contact #0
First name: Pq
Last name: q
Phone number: 1095516483
Contact #1
First name: test
Last name: 2
Phone number: 8901234
There are some issues on your code:
1> Scope of variable a, b, con should be in main function, you are override the scope of variable a, b from main function scope to in-case scope by re-declare them inside case block code.
int b = b+1;
int a = (n-1);
struct contact *con = (struct contact*)malloc(n*sizeof(struct contact));
In case block code, please just assign them new value:
b = b+1;
a = (n-1);
con = (struct contact*)malloc(n*sizeof(struct contact));
2> The con pointer should be changed from
struct contact *con[200];
by define a initialized size for it. and if the size is out of range, we need to realloc the con in case of add new item:
int size = 200;
struct contact *con = (struct contact *)malloc(size * sizeof(struct contact));
// in case of the size of out of range
if (n >= size)
{
size = size * 2;
con = (struct contact*)realloc(con, size * sizeof(struct contact));
}
Here is my solution:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct contact { // Data structure that holds contact information
char FirstName[10]; // Array for first name
char LastName[10]; // Array for last name
int PhoneNum; // Phone number
};
int main() {
// Setting up variables
int Function = 0;
int Choice = 0;
char FName[200][10];
char LName[200][10];
int PNum = 0;
int n = 1;
int size = 200;
struct contact *con = (struct contact *)malloc(size * sizeof(struct contact));
int b = 0, a = 0;
do { // Will loop through the main function until the user decides to exit the program
// Prints out the main menu of the phone book
printf("\nPhone Book");
printf("\n[1] Add a contact");
printf("\n[2] Delete a contact");
printf("\n[3] Show contacts");
printf("\n[4] Exit program");
printf("\n\nWhat function would you like to use?\n"); // Asks for user input
scanf("%d", &Choice);
switch (Choice) {
case 1: // Case to add a contact into the phone book
printf("\nYou chose to add a contact.");
printf("\nFirst name: ");
scanf("%s", &FName[b]);
printf("\nLast name: ");
scanf("%s", &LName[b]);
printf("\nPhone number (Numbers only): ");
scanf("%d", &PNum);
printf("\nRecord added to the phone book");
// Records the information given into the structure
if (n >= size)
{
size = size * 2;
con = (struct contact*)realloc(con, size * sizeof(struct contact));
}
a = (n - 1);
printf("\na is %d", a);
strcpy(con[a].FirstName, FName[b]);
strcpy(con[a].LastName, LName[b]);
con[a].PhoneNum = PNum;
b = (b + 1);
n++;
printf("\nn is %d", n);
// Prints out the given information
printf("\nNew contact:");
printf("\nFirst name: %s", con[a].FirstName);
printf("\nLast name: %s", con[a].LastName);
printf("\nPhone number: %d", con[a].PhoneNum);
printf("\n");
break;
case 2: // Case to delete a contact from the phone book
printf("\nYou chose to delete a contact.");
break;
case 3: // Case to see all of the entered contacts
printf("\nYou chose to show the contacts.");
for (a = 0; a < (n - 1); a++) {
printf("\nContact #%d", a);
printf("\nFirst name: %s", con[a].FirstName);
printf("\nLast name: %s", con[a].LastName);
printf("\nPhone number: %d", con[a].PhoneNum);
printf("\n");
}
break;
case 4:
printf("Goodbye!");
break;
}
} while (Choice != 4);
return 0;
}
You're defining a new variable called con inside of your switch statement. This variable mask the variable of the same name defined at the top of the function. This is the one you're adding a record to.
When you later go to print the list, it's actually reading from this inner con. However, since you're re-entering the switch statement, the variable's contents is uninitialized because you jump over the initializer because of the switch and you invoke undefined behavior. You get "lucky" that you're able to print the last instance read in, probably because the variable still happened to contain the old value from the last iteration.
Also, the outer version of con is declared as an array of pointers. You probably want just a pointer here so it can point to a dynamically allocated array.
So define con like this:
struct contact *con = NULL;
And change the "add" case to not define the variable. Also, you don't need separate variables to read in the values. You can read them directly into an instance of the struct:
n++;
con = realloc(n*sizeof(struct contact));
printf("\nYou chose to add a contact.");
printf("\nFirst name: ");
scanf("%s", con[n-1].FirstName);
printf("\nLast name: ");
scanf("%s", con[n-1].LastName);
printf("\nPhone number (Numbers only): ");
scanf("%d", &con[n-1].PhoneNum );
printf("\nRecord added to the phone book");
printf("\nn is %d", n);
printf("\nNew contact:");
printf("\nFirst name: %s", con[n-1].FirstName);
printf("\nLast name: %s", con[n-1].LastName);
printf("\nPhone number: %d", con[n-1].PhoneNum);
printf("\n");
Then you'll also need to move a down to the "show" portion:
for( int a = 0; a < (n - 1); a++){
As #Some programmer dude suggested you need to reallocate your array if you want to make it bigger and keep your data.
So essentially something like this
// Records the information given into the structure
struct contact *con = (struct contact*)malloc(n*sizeof(struct contact));
should be at the start of your main function like so:
// dynamically allocate memory which can hold e.g. 1 struct
struct contact *con = malloc(sizeof(struct contact));
and when you resize your memory chunk on the heap, that is what malloc does for you, you should resize or realloc it like so:
// grow array
con = realloc(con, n * sizeof(struct concat));
Read the man page for further Information: realloc man page, or type man 3 realloc or man realloc on your terminal.
EDIT
As #dbush suggested
You declare 2 variables named con. At the start struct contact *con[200]; and in the switch statement you allocate memory and save it into struct concat *con.
So the reason why you still get undefined results on option "3" (show all contact) is, that you actually are reading the contacts from the array struct contact *con[200]; and not from the memory pointed by struct contact *con;
Greetings

How do I search through a structure and display certain info after it has been entered by the user?

For my assignment I am to create a structure that allows the user to enter student info (ID, DOB, & Phone number). I have no problem doing this that is quite simple. Now I need to search through that enter info using the student ID to display that students corresponding DOB and phone number, this is the problem that I am having trouble working with. If you see any other problems with my program please let me know what is wrong and why I should change so I can learn from my mistakes.
Thank you.
#include <stdio.h>
#include <stdlib.h>
struct infoStruct
{
int studentID;
int year;
int month;
int day;
int phone;
int end;
};
int main (void)
{
int students = 0;
int infoArray [students];
struct infoStruct info;
int studentID;
int year;
int month;
int day;
int phone;
int end;
while (info.end != -1) {
students = students + 1;
printf("Enter student information (ID, day, month, year, phone)\n");
printf("Enter -1 following the phone number to end the process to continue enter 0\n");
scanf("%d %d %d %d %d %d", &info.studentID, &info.day, &info.month, &info.year, &info.phone, &info.end);
}
if (info.end = -1){
printf("You entered %d student(s)\n", students);
}
//Student Search
printf("Please enter the student ID of the student your looking for\n.");
scanf("%d", info.studentID);
printf(" DOB: %d %d %d, Phone: %d", info.month, info.day, info.year, info.phone);
}
info.end is not initialized before while (info.end != -1). Initiliaze all your variable (studentID...) and structure.
if (info.end = -1) is an assignment !
Use : if (info.end == -1) I prefer to use if (-1 == info.end) (if you had use : only = instead of == you would have get an error). (Yoda trick ^^)
And you have to use an array of struct in order to save every student (because you are continuously erasing the previous student information).
It's your homework, I won't do the work for you ;)
I'll leave most of the coding to you, as this is homework, but here is what you need to change to get this to work.
First of all, if you want to store multiple students info is going to need to be an array
static int MAX_STUDENTS = 50;
struct infoStruct info[MAX_STUDENTS];
and then you scan each student into a seperate part of the struct
scanf("%d %d %d %d %d %d", &info[students-1].studentID, &info[students-1].day, &info[students-1].month, &info[students-1].year, &info[students-1].phone, &info[students-1].end);
then you need to ensure that the end condition is checked properly (check the latest info[x].end)
It would also be wise to check you still have some room in the array before trying to add more.
with those done you are storing the students correctly.
as for searching, you need to scan the id to search into a seperate int.
then loop through the array (info[x]) and search each element's studentID against the search ID. When you have a match, print it out.
Edit:
its also worth considering storing the phone number as a "string" rather than an int, alot of phone numbers start with "0", but an int would remove the 0.
(so the phone number 012345, would become, 12345)

How to approach and optimize code in C

I am new to C and very much interested in knowing how to approach any problem which has more than 3 or 4 functions, I always look at the output required and manipulate my code calling functions inside other functions and getting the required output.
Below is my logic for finding a students record through his Id first & then Username.
This code according to my professor has an excessive logic and is lacking in many ways, if someone could assist me in how should I approach any problem in C or in any other language it would be of great help for me as a beginner and yes I do write pseudo code first.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
int id; //Assuming student id to be unique
int age;
char *userName; //Assuming student userName to be unique
char *dept;
}student; // Alias "student" created for struct
student* createstruct(); // All function prototype declared
student* createArray();
void addstruct(student* s2);
void searchChar(student* s2,int num);
void searchInt(student* s2,int num);
student* createstruct() // function createStruct() to malloc data of struct student.
{
student *s;
s = (student*)malloc(sizeof(student));
s->userName = (char*)malloc(sizeof(char)*32);
s->dept = (char*)malloc(sizeof(char)*32);
printf("please enter id ");
scanf("%d",&s->id);
printf("please enter age ");
scanf("%d",&s->age);
printf("please enter userName ");
scanf("%31s",s->userName);
printf("please enter department ");
scanf("%31s",s->dept);
printf("\n");
return s;
}
student* createArray()
{
student *arr; //declaration of arr poiter, type struct student
arr = (student*)malloc(sizeof(student)*10); // memory allocated for a size of 10
return arr;
}
void addstruct(student *s2) // function for adding data to the structures in array
{
int i,num;
student* s1;
printf("please enter the number of records to add:");
scanf("%d",&num);
printf("\n");
if(num>0 && num<11)
{
for(i=0;i<num;i++) // if user want to enter 5 records loop will only run 5 times
{
s1 = createstruct();
s2[i].id = s1->id; // traversing each element of array and filling in struct data
s2[i].age = s1->age;
s2[i].userName = s1->userName;
s2[i].dept= s1->dept;
}
}
else if(num>10) // if user enters more than 10
{
for(i=0;i<10;i++) // loop will still run only 10 times
{
s1 = createstruct();
s2[i].id = s1->id;
s2[i].age = s1->age;
s2[i].userName = s1->userName;
s2[i].dept = s1->dept;
}
printf("Array is full"); // Array is full after taking 10 records
printf("\n");
}
searchInt(s2,num); // Calling searchInt() function to search for an integer in records
searchChar(s2,num); // Calling searchChar() function to search for a string in records
free(s1);
free(s2);
}
void searchChar(student* s2,int num) // function for searching a string in records of structure
{
char *c;
int i;
c = (char*)malloc(sizeof(char)*32);
printf("please enter userName to search ");
scanf("%31s",c);
printf("\n");
for (i=0;i<num;i++) //num is the number of struct records entered by user
{
if ((strcmp(s2[i].userName,c)==0)) //using strcmp for comparing strings
{
printf("struct variables are %d, %d, %s, %s\n", s2[i].id,s2[i].age,s2[i].userName,s2[i].dept);
break;
}
else if(i == num-1)
{
printf("nothing in userName matches: <%s>\n",c);
break;
}
}
}
void searchInt(student* s2,int num) //searchs for an integer and prints the entire structure
{
int i,z;
printf("please enter id to search ");
scanf("%d",&z);
printf("\n");
for (i=0;i<num;i++)
{
if (s2[i].id == z)
{
printf("struct variables are %d, %d, %s, %s\n\n", s2[i].id,s2[i].age,s2[i].userName,s2[i].dept);
break;
}
else if(i == num-1)
{
printf("nothing in id matches: <%d>\n\n",z);
break;
}
}
}
int main(void)
{
student *s2;
s2 = createArray();
addstruct(s2);
return 0;
}
I'm not going to go into optimizing, because if you wanted better theoretical performance you would probably go with different data structures, such as ordered arrays/lists, trees, hash tables or some kind of indexing... None of that is relevant in this case, because you have a simple program dealing with a small amount of data.
But I am going to tell you about the "excessive logic" your professor mentioned, taking your searchInt function as an example:
for (i=0;i<num;i++)
{
if (s2[i].id == z)
{
printf("struct variables are %d, %d, %s, %s\n\n", s2[i].id,s2[i].age,s2[i].userName,s2[i].dept);
break;
}
else if(i == num-1)
{
printf("nothing in id matches: <%d>\n\n",z);
break;
}
}
The thing here is that every time around the loop you're testing to see if you're at the last element in the loop. But the loop already does that. So you're doing it twice, and to make it worse, you're doing a subtraction (which may or may not be optimized into a register by the compiler).
What you would normally do is something like this:
int i;
student *s = NULL;
for( i = 0; i < num; i++ )
{
if( s2[i].id == z ) {
s = &s2[i];
break;
}
}
if( s != NULL ) {
printf( "struct variables are %d, %d, %s, %s\n\n",
s->id, s->age, s->userName, s->dept );
} else {
printf("nothing in id matches: <%d>\n\n",z);
}
See that you only need to have some way of knowing that the loop found something. You wait for the loop to finish before you test whether it found something.
In this case I used a pointer to indicate success, because I could then use the pointer to access the relevant record without having to index back into the array and clutter the code. You won't always use pointers.
Sometimes you set a flag, sometimes you store the array index, sometimes you just return from the function (and if the loop falls through you know it didn't find anything).
Programming is about making sensible choices for the problem you are solving. Only optimize when you need to, don't over-complicate a problem, and always try to write code that is easy to read/understand.

Resources