input number in structs - check it already exists in file - c

I'm trying to write a data validation function, where the ID number has to be a unique number in an array of structs. Every time I try to save a valid ID number, the program is crashing - why is this happening?
this is the struct, and the declaration of an array of structs:
struct customer { // set up customer template //
char name [MAXNAM];
char surname [MAXNAM];
int idnum [MAX_ID];
};
struct customer data_cus[MAXCUS];
this is within the main program:
printf ("Please Enter ID Card, [NOTE! Only numbers are allowed!]\n");
int checkID;
while ((scanf ("%d",&checkID) == 0 || customerID(checkID, count)==1))
{
printf ("This ID is already taken! Please enter unique ID!\n");
while (getchar()!='\n')
{
continue;
}
*data_cus[count].idnum = checkID;
}
Here is the function used to check id the ID already exists in file:
int customerID (int cCheck, int count)
{
FILE * pcustomer;
int size = sizeof (struct customer);
struct customer temp;
rewind (pcustomer);
while (fread (&temp,size,count,pcustomer)==1)
{
if (*temp.idnum == cCheck)
{
return 1;
}
}
return 0;
}
Thank you for your help!

idnum is an array of integers. Is that really what you want, not just a simple integer?
I'm pretty sure that if you remove [MAX_ID] from your struct, things will work much better.
However, I reserve the right to alter the motivation as to why it's crashing once we see the code that is after the ID check code.

scanf returns the number of successfully matched items, I think you mean:
while (scanf ("%d",&checkID) == 1 && customerID(checkID, count)==1).
You should make sure count doesn't exceed MAXCUS only increment when you successfully read a unique ID, and you don't need an array of integer for idnum since each record has only one.
Also the function customerID is obviously wrong, here you read more than one customer structure and write to temp which is a single customer structure, this is most likely why it crashes:
while (fread (&temp,size,count,pcustomer)==1)
It's much easier if you keep track of IDs in an array instead of reading from file every time you want to check if a new ID is unique.

Related

Sort a list of members in ascending and descending order

I'm creating a simple database containing students with the following information: first name, last name, personalnumber, study type (permanent or part time student).
If user hits "showasc" the program asks whether to show students in ascending order by their first name or last name.
Managed to sort the members by name, but the problem is that only the names gets sorted while the rest of the information about the student doesn't, it stays in the order it was created. Bare in mind that I'm still very new to programming.
This is the part of the code that sorts the names:
else if(strcmp(command, "showasc") == 0)
{
char input[15];
printf("Show in ascendant order by first name or last name?\n ");
scanf("%s", input);
struct Student copyOfListOfStudents[20];
memcpy(&copyOfListOfStudents, &listOfStudents, sizeof(listOfStudents));
int i, j;
char s[100];
for(i=0;i<20;i++)
{
for(j=i+1;j<20;j++)
{
if(copyOfListOfStudents[i].valid && copyOfListOfStudents[j].valid)
{
if(strcmp(copyOfListOfStudents[i].firstName,copyOfListOfStudents[j].firstName) > 0)
{
strcpy(s,copyOfListOfStudents[i].firstName);
strcpy(copyOfListOfStudents[i].firstName,copyOfListOfStudents[j].firstName);
strcpy(copyOfListOfStudents[j].firstName,s);
}
}
else
break;
}
}
printStudents(copyOfListOfStudents);
So for example, if user adds the the following students:
Lol Ipop, 7802034567, Full time
Anna Banana, 9901024356, Part time
And then wants to show students sorted by first name, this is what happens:
Anna Ipop, 7802034567, Full time
Lol Banana, 9901024356, Part time
How do I fix this problem?
(I hope that the code I'm showing is sufficient!)
You have already identified the problem. In your sorting algorithm, the names get sorted, but the other values are not.
To fix the issue, you need to change this part to swap the whole student struct:
if(strcmp(copyOfListOfStudents[i].firstName,copyOfListOfStudents[j].firstName) > 0)
{
Student tmp;
memcpy(tmp, copyOfListOfStudents[i], sizeof(Student));
memcpy(copyOfListOfStudents[i], copyOfListOfStudents[j], sizeof(Student));
memcpy(copyOfListOfStudents[j], tmp, sizeof(Student));
}
Code is untested...

Assigning result of getchar() to a struct member pointer

I've been trying to create a function that assigns getchar() to a pointer (p_customers) to a struct member (last_name), but I keep getting compiler errors or a segmentation fault. Here's what I have so far:
/* A customer account receivable record */
struct customer
{
char last_name[MAX_NAME_LENGTH+1]; /* Customer last name */
float amount; /* Amount the customer owes */
int priority; /* Customer priority */
};
void get_customers(struct customer *p_customers_start,
int number_customers)
{
struct customer *p_customers; /* Points to the account receivable */
/* customer */
char *p_last_name; /* Points to the last name character */
/* Loop processing account receivable customer data */
for (p_customers=p_customers_start;
(p_customers-p_customers_start) < number_customers;
p_customers++)
{
/* Print out account receivable customer */
printf( "\nCustomer #%d:",
*p_customers - *p_customers_start + 1);
/* Obtain last name of customer */
printf(" \nEnter the customer's last name: ");
getchar();
do
{
p_customers->last_name++ = getchar();
}
while(*p_last_name != NEW_LINE);
p_last_name = END_OF_STRING;
}
}
I have no clue what the problem is but could anyone clue me in on how to fix it?
Can you post the complete code? Above code is too obscure to give any hint correct analysis. However, I could make below comments based on assumptions:
p_customers->last_name and p_last_name mayn't be referring to the same location. As a result, "last_name++ = getchar()" keeps on reading the characters and tries to put in addresses beyond its limit. This causes segfault.
Subtraction is allowed on numeric types, but structures can't be subtracted from one another. So when you say "*p_customers - *p_customers_start + 1" there is no meaning for "*p_customers_start+1" and "*p_customers - <previous_expression>".
For e.g., if a structure has employee records, what does subtraction of one from another mean?
I hope you wanted current customer number. If yes, you can use just "p_customers - p_customers_start +1".
The name of array is a "constant pointer". So it can never be changed. When you say "p_customers->last_name++", you are trying to change a constant (pointer), which is not allowed.
Consider a situation where the user first gives NEWLINE for last name before giving actual last name. And in case that last name length is greater than MAX_NAME_LENGTH, you end up overwriting an area beyond the limits of array last_name. When that happens you are getting the segmentation fault.

How to match a varible in a struct array, then the position

first post here. Somewhat new to coding aswell.
Right now I'm at halt. What I need to do is get input from the user of an employee id then find it in a struct array variable, and keep asking until a correct one is entered.. all in a do-while loop. Then I need to print the salary of that employee and update it to something new.
I'm having a hard time trying to figure out how to search the struct array for that employee ID, then when that's done, saving what position the struct array in is so I can print THAT employees salary, then update it to something else.
Heres some code to show the struct array
#define SIZE 4
struct employee{
int id[SIZE] ;
int age[SIZE];
double salary[SIZE];
};
/* main program */
int main(void) {
struct employee emp[SIZE] = { 0 };
Here is some to show the code to what I've done
case 3: //Update Employee
printf("Update Employee Salary\n");
printf("======================\n");
int employid;
do{
printf("Enter Employee ID: ");
scanf("%d", &employid);
}while(employid != emp->id[SIZE]);
printf("The current salary is %lf", emp->salary[SIZE]);
break;
I know this is wrong. The While just looks at the current position, and the printf only prints the salary of the current arrays employee aswell.
Any help would be appreciated.
Bonus:
Notice when I'm assigning/working with my struct, I have to put "->" to actually tell my compiler that I'm pointing to that specific variable in the struct. Usually I put "." and have no idea why I need to put ->.. I just found the fix online.
Usually it would be "emp.id[SIZE];"
NOT "emp->id[SIZE];"
Anyone have any ideas?
Thanks all
Considering that your input to the structure is right, there are few things you can do.
The thing is you need to realize that you need a while/for loop to iterate over the employee array.
Second thing is, here you are not comparing it correctly. You are just comparing with the first element.
Now what you can do is
bool found = false;
int foundEmpId = -1;
do{
printf("Enter Employee ID: ");
scanf("%d", &employid);
for(size_t i = 0; i< SIZE; i++)
if( emp[i].id == employid ){
foundEmpId = employid;
found = true;
break;
}
}while(!found);
// do your work.
Now this is simply the O(n) searching.
Apart from that there is one thing you can see in my code. I am using emp[i].id. Here the structure I considered is
struct employee{
int id ;
int age;
double salary;
};
If you think of an employee these are the attributes you need to add over here. What you did was working with different groups of employees.
Here you will work like this
struct employee emps[SIZE];
...
...
// the attributes of employee will be inputted like this
// if( scanf("%d",&emps[i].id) == 1 ) { /* */ }
...
Why did you need emp->id[SIZE] here?
Dissecting the emp->id[SIZE] is equivalent to (*emp).id[SIZE] or more precisly emp[0].id[SIZE].
To summarize the problems/suggestion:-
P The portion of the code that you have shown it shows that you are not comparing with all the employees.
P You have created groups of employees. But you are not iterating or using all the groups. You just worked on first employee group.
S Instead of using the array of employee groups you should simply use array of employees. Or Even if you do use groups of enployess then just one group will suffice.

How to properly pass array pointers to function in C

A different question may have been asked about this program, however in this C code I have three functions: one to print records, one to add a record and one to delete a record.
What I don't understand is why the (add) and (delete) do not make changes in the main function, so when I use the print all records function it prints changes, but it doesn't show changes, what is wrong?
Details are in the comments, please feel free to run the code to visualise the problem.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*The program is to store student record (firstname,lastname and score), it should not be more
than 15 records and the names should not be more than 20 chars, array pointersnare being used
instead of arrays, the (add_record) and (delete_record) functions must
do thsi change in the main function, so when you print all records, the changes are shown*/
void print_records(char **firstname,char **lastname,float *score,int *the_size)
{
int i;
printf("Printing All record(s)...\n");
for (i=0;i<*the_size;i++) /*loop to print records of all arrays in correct format*/
{
printf("Firstname : %s, Lastname : %s, Score : %f\n",firstname[i],lastname[i],score[i]);
}
}
void add_new_record (char **firstname,char **lastname,float *score,int the_size)
{
printf("Add new record in the format :\nFirstname Lastname Score\n");
/*the strategy here is to check if all 15 elemts are used, if they are, use realloc
to add one more, if not add the record after the last record*/
if (the_size == 15)
{
firstname=realloc(firstname,16*sizeof(char*));
firstname[15]=malloc((20+1)*sizeof(char));
lastname=realloc(lastname,16*sizeof(char*));
lastname[15]=malloc((20+1)*sizeof(char));
score=realloc(score,16*sizeof(float));
scanf("%s %s %f",firstname[15],lastname[15],&score[15]);
printf("New Record Added Successfully !\n");
printf("Firstname : %s, Lastname : %s, Score : %f\n",firstname[15],lastname[15],score[15]);
}
else if (the_size<15)
{
scanf("%s %s %f",firstname[the_size],lastname[the_size],&score[the_size]);
printf("New Record Added Successfully !\n");
printf("Firstname : %s, Lastname : %s, Score : %f\n",firstname[the_size],lastname[the_size],score[the_size]);
}
}
void delete_record (char **firstname,char **lastname,float *score,int the_size)
{
char *str=malloc(20*sizeof(char)); /*String entered by user must be 20 or less chars*/
int i,ctr=0;
char *temp_first=malloc(20*sizeof(char));/*temp array to swap firstname must be 20 or less chars*/
char *temp_last=malloc(20*sizeof(char)); /*temp array to swap lastname must be 20 or less chars*/
float temp_score;/*ctr is the counter used to check if there are no matchs in the end*/
printf("Enter the lastname of record(s) to delete : ");
scanf("%s",str);
/* the strategy here is to move the element to be deleted to the last index and use
relloc to shrink the size by 1 (-1) */
for (i=0;i< the_size;i++)
{
if (strcmp(str,lastname[i])==0)
{
printf("Deleting Record for %s %s...\n",firstname[i],lastname[i]);
temp_score=score[i];
score[i]=score[the_size-1];
score[the_size-1]=temp_score;
strcpy(temp_first, firstname[i]); /*using strcpy function to swap strings*/
strcpy(firstname[i], firstname[the_size-1]);
strcpy(firstname[the_size-1], temp_first);
strcpy(temp_last, lastname[i]);
strcpy(lastname[i], lastname[the_size-1]);
strcpy(lastname[the_size-1], temp_last);
score=realloc(score,(the_size-1)*sizeof(float));
firstname=realloc(firstname,(the_size-1)*sizeof(char*));
lastname=realloc(lastname,(the_size-1)*sizeof(char*));
ctr++;
the_size--;
}
}
if (!ctr) /*if ctr=0 (no increment), then print,there is no match*/
{
printf ("Sorry, no available record for %s",str);
}
free(temp_first);
free(temp_last);
free(str);
}
void main()
{
char **firstname;
char **lastname;
float *score;
int number_of_records,i,j=-1,ctr=1,row=15,col=20;
/*ctr is to keep track of the student's number (makes it easier to
the user), it starts with (1)*/
firstname=malloc(row*sizeof(char*));
for(i=0;i<row;i++)
{
firstname[i]=malloc((col+1)*sizeof(char));
}
lastname=malloc(row*sizeof(char*));
for(i=0;i<row;i++)
{
lastname[i]=malloc((col+1)*sizeof(char));
}
printf("\nPlease indicate number of records you want to enter (min 2, max 15): ");
scanf("%d",&number_of_records);
score=malloc(row*sizeof(float));
printf("\nPlease input records of students\n(enter a new line after"
"each record), with following format:\nfirst name last name score ");
for (i=0;i<number_of_records;i++)
{
printf("\nEnter record for student %d : ",ctr);
scanf("%s %s %f",firstname[i],lastname[i],&score[i]);
ctr++; /*ctr is to keep track of student number
(makes it easy to the user) */
}
while (j!=0) /*Main menu will keep looping after using a function as long as j is not 0
When the user enters 0 (zero) the loop will stop and therefore the program will terminate*/
{
printf("\nSelect desired function by pressing the corresponding key number\n");
printf("\n********** Main Menu **********\n");
printf("\n>>> Print records (press 1)\n");
printf("\n>>> Add a new Record (press 2 )\n");
printf("\n>>> delete record (press 3)\n");
printf("\n>>> Exit the program (press 0)\n");
scanf("%d",&j); /*getting j from the user (j is used for selection and for the while loop)*/
if (j==1)
{
print_records(firstname,lastname,score,&number_of_records);
}
else if (j==2)
{
add_new_record(firstname,lastname,score,number_of_records);
}
else if (j==3)
{
delete_record(firstname,lastname,score,number_of_records);
}
else if (j==0)
{
printf("Exitting program ...\n");
}
}
}
As other answers have observed, in C, all arguments are passed by value. That means the function gets a copy of the caller's value, therefore changes to that value are not visible to the caller. In other words, given
void f(any_type arg) {
arg = any_value;
}
The caller will never detect any change, regardless of what type any_type is or what value any_value is. Note carefully, however, the difference between that, and this:
void f(any_type *arg) {
*arg = any_value;
}
In that case, it is not the function argument (a pointer) that is being modified, it is the thing pointed to. The argument is a copy of the caller's value, so the two point to the same thing. The caller cannot detect changes to the argument, but after the call, it can detect changes to the thing it points to.
Your code exhibits some issues of this type, some of them responsible for your main problems. Most importantly, these have to do with your record keeping on the number of elements in your list (variable number_of_records in main()). Your addition and deletion functions work more or less ok, except that they cannot communicate the revised list size back to main.
There are additional issues in add_new_record() when there are already 15 records; if you can, I would just disallow that case. If you must support it, then you have multiple things to clean up. Some of them to do with pass-by-value issues, others to do with what your code should do when the list initially contains 16 or more records.
Update:
Since you're having so much trouble working this out, here's a revised version of delete_record(). It implements a lot more than the minimal changes required to get the desired output upon record deletion, as there was in fact a goodly number of other issues that I might as well call out as long as I'm going to the trouble. See new and modified comments.
/*
* Can realloc() *firstname, *lastname, and *score and return the updated
* values to the caller. Most importantly, can update *the_size and have
* the caller see the result.
*/
void delete_record (char ***firstname, char ***lastname, float **score, int *the_size)
{
int i;
int initial_size = *the_size;
char str[21]; /* no need to malloc a fixed-length local array */
printf("Enter the lastname of record(s) to delete : ");
fflush(stdout); /* The prompt might not appear if you don't flush */
/*
* Note the field width in the format below. Without it, a user can
* easily cause a buffer overflow.
*/
scanf("%20s", str);
/*
* The strategy for each element to delete (there may be more than one)
* is to free the element's name components (else their allocated memory
* leaks), copy the last (at that time) element's components into
* place (for the name components, just the pointers), and later
* realloc to shrink the overall size to exactly fit the remaining
* elements (once we know how many that is).
*/
for (i = 0; i < *the_size; )
{
if (strcmp(str, (*lastname)[i]) == 0)
{
printf("Deleting Record for %s %s...\n", (*firstname)[i], (*lastname)[i]);
free((*firstname)[i]);
free((*lastname)[i]);
(*firstname)[i] = (*firstname)[*the_size - 1];
(*lastname)[i] = (*lastname)[*the_size - 1];
(*score)[i] = (*score)[*the_size - 1];
*the_size -= 1; /* not the same as *the_size-- */
/* don't increment i, else we miss testing the new i'th element */
} else {
i += 1;
}
}
if (*the_size != initial_size)
{
void *temp;
/*
* Always check the return value of realloc(), even when you're
* shrinking the allocation. Usually, though, you'd want at least
* some kind of diagnostic in the event of failure.
*/
temp = realloc(*firstname, sizeof(char *) * (*the_size));
if (temp)
{
*firstname = temp;
}
temp = realloc(*lastname, sizeof(char *) * (*the_size));
if (temp)
{
*lastname = temp;
}
temp = realloc(*score, sizeof(float) * (*the_size));
if (temp)
{
*score = temp;
}
}
else /* there is no match */
{
printf ("Sorry, no available record for %s",str);
}
}
Your main() would call that like so:
delete_record(&firstname, &lastname, &score, &number_of_records);
Similar changes are needed to add_record(), though you do have the separate issue I already called out there with increasing the number of entries past 16.
Additionally, you're making extra work for yourself by using separate arrays of first name, last name, and score. It would be much easier to define a struct encompassing all three, and use just one dynamic array whose elements are instances of that struct.
First, you're declaring the arguments of add_ as pointers (presumably because you want the function to change the value of the variables whose addresses you pass):
add_new_record (char **firstname, char **lastname, float *score, int the_size) {
But then you simply assign the locals, not the things they point to:
firstname = realloc(firstname,16*sizeof(char*));
score = realloc(score,16*sizeof(float));
If you want to change the variables to which these point, you have to
dereference them for assignment:
*firstname = malloc(16);
*score = 1.0;
Now the original firstname pointer points to a valid chunk of memory (presumably you'll want to strcpy() some actual name there), and the original float variable to which score points is now 1.0.
There are other problems, but that's the primary reason this function doesn't change what you think it should, because you didn't tell it to.

( C ) Cannot find reason for Core Dump Error with Dynamic Allocating Structs

Disclaimer: This is my first question on StackOverflow and I'm a novice programmer, so my apologies ahead of time if you are appalled by my code or if I don't post my question appropriately.
Anyways, I'm working on a gradebook with dynamically allocated structures. I've divided the gradebook into three structues, a student structure (student name, student id), a course structure (course name, course id), and an enroll structure(student id, course id, grade).
Problem: I'm able to input as many grade as I need without errors for the first student. When I try to input a grade for the second student my program core dumps. I've checked all the variables involved to see if they are passed to my function appropriately and they are. The following is my Enroll structure and my add grade function.
typedef struct {
int Student_ID;
int Course_ID;
int *Grade;
int GradeCount;
} Enroll_Database;
Function...
void addGrade(Enroll_Database *Enroll)
{
int i = 0, j = 0, b, Course_Num, Student_Num, Grade;
printf("Enter Course Number: ");
scanf("%d", &Course_Num);
printf("Enter Student ID: ");
scanf("%d", &Student_Num);
/* For loop that traverses through the Enroll array until until it encounters
nothing in the Course ID */
for(i = 0; Enroll[i].Course_ID != 0; i++)
{
/* if the Student Number and the Course Number are equal to their
appropriate Enroll element, then ask user to input Grade */
if(Enroll[i].Student_ID == Student_Num && Enroll[i].Course_ID == Course_Num)
{
printf("Enter Grade: ");
scanf("%d", &Grade);
if(Enroll[i].GradeCount == 0)
{
Enroll->Grade = (int *) malloc(sizeof(int));
Enroll[i].Grade[Enroll[i].GradeCount] = Grade; //core dumps
Enroll[i].GradeCount++;
}
else
{
Enroll->Grade = (int *) realloc(Enroll->Grade, sizeof(int));
Enroll[i].Grade[Enroll[i].GradeCount] = Grade; //core dumps
Enroll[i].GradeCount++;
}
}
}
}
I've ran multiple checks and the core dump occurs after I malloc/realloc and assign user input to the grade value in the enroll structure.
I'd greatly appreciate any help, and I'm sorry again if my code is unreadable or I formatted wrong.
Thanks!
This only allocates space for one element,. and also it reallocates the wrong pointer:
Enroll->Grade = (int *) realloc(Enroll->Grade, sizeof(int));
It could be fixed like this:
Enroll[i].Grade = realloc(Enroll[i].Grade, sizeof(int) * (Enroll[i].GradeCount + 1));
Remembering that X->Y, (*X).Y, and X[0].Y all mean the same thing: your original version actually reallocated Enroll[0].Grade, instead of Enroll[i].Grade.
(The rest of this answer is some possible style improvement suggestions:)
To avoid this sort of error, I'd personally write just after the scanf:
Enroll_Database *found = &Enroll[i];
and then use found-> everywhere instead of Enroll[i]. Alternatively I'd consider having a separate function to actually add the grade (which is called once the database entry has been found).
Now, if you initialize Enroll[i].Grade to NULL when setting up the database, you actually wouldn't need this if...else statement. Since the behaviour of realloc(NULL, X) is the same as malloc(X), the same code would handle both situations.
Note that in C you should not cast the value returned by malloc and friends.
Another thing to bear in mind is that the style X = realloc(X, ... does not allow you to recover from allocation failure. To write robust code you need to take some sensible action when a malloc-family function returns NULL; this could be as simple as printing a message and calling exit.

Resources