Sorting an incrementing list in C - c

I am trying to create a list of employees and sort the list based on age each time I add a new employee. The problem I am having is that only a list of 1 employee is "sorted". If I add another employee suddenly all of the ages become 0. Here is my code:
#include <stdio.h>
#include <string.h>
#include "employee.h"
int i = 0;
unsigned int employee_get_num (struct employee* list)
{
unsigned int i;
for (i=0; list[i].name[0]; i++);
return i;
}
void employee_sort (struct employee* list)
{
int n = i;
int I, j;
int tmp;
printf("There are %d employees\n", n);
for(I=0; I<n; I++)
printf("Age: %d\n", list[i-1].age);
for(I=0; I<(n-1); I++)
for(j=0; j<n-I-1; j++)
if(list[j].age > list[j+1].age){
tmp = list[j].age;
list[j].age = list[j+1].age;
list[j+1].age = tmp;
}
printf("Sorted list:\n");
for(I=0; I<n; I++)
printf("%d\n", list[i-1].age); // only printing zeros for some reason
}
void employee_add (struct employee* list)
{
i = i+1; // i is global, keeps track of employees
char first[128];
char last[128];
char space[] = " ";
printf ("First Name: ");
scanf ("%s", first);
printf("Last Name: ");
scanf ("%s", last);
// Concatenate first and last name into one string
strcpy(list[i-1].name, first);
strcat(list[i-1].name, space);
strcat(list[i-1].name, last);
printf (" Age: ");
scanf("%u", &(list->age));
printf ("Wage: ");
scanf("%u", &(list->wage));
employee_sort(&list[i-1]);
}
I think that I might not be increasing correctly.
Header file:
#ifndef _employee_h_
#define _employee_h_
struct employee {
char name[128];
unsigned int age;
unsigned int wage;
};
unsigned int employee_get_num (struct employee* list);
void employee_print (struct employee* e);
void employee_print_all (struct employee* list);
void employee_sort (struct employee* array);
void employee_add (struct employee* list);
void employee_delete (struct employee* list);
#endif
main() (in menu.c)
int main (unsigned int argc, char** argv)
{
struct employee list[MAX];
unsigned int running = 1;
/* Set all bits in the employee array to zero */
memset (list, 0, MAX*sizeof(struct employee));
while (running) {
switch (print_menu()) {
case OPTION_ADD:
employee_add(list);
break;
case OPTION_DEL:
employee_delete(list);
break;
case OPTION_LIST:
employee_print_all(list);
break;
case OPTION_QUIT:
running = 0;
break;
};
}
return 0;
}
I left out the rest of menu.c because it only prints the menu which you'll see below.
The output should look like:
[1] Add New Employee
[2] Delete an Employee
[3] List All by Age (Acending)
[4] Quit
------------------------
Selection: 1
First Name: Bob
Last Name: Smith
Age: 40
Wage: 60000
There are 1 employees
Age: 40
Sorted list:
40
[1] Add New Employee
[2] Delete an Employee
[3] List All by Age (Acending)
[4] Quit
------------------------
Selection: 1
First Name: John
Last Name: Connor
Age: 35
Wage: 62000
There are 2 employees
Age: 40
Age: 35
Sorted list:
35
40
I added some extra print statements just to show what it is supposed to be doing.
I'm also worried that it is only going to sort the age and not the rest of the information

Firstly in employee_add when calling employee_sort you are passing the last struct element. Pass the first employee. Change last line in employee_add to
employee_sort(list);
Next problem
If I add another employee suddenly all of the ages become 0
No it doesn't. In employee_sort in the for loop before sorting, change it to
printf("There are %d employees\n", n);
for(I=0; I<n; I++)
printf("Age: %d\n", list[I].age);
Next
While taking age and wage input change it to
scanf("%u", &(list[i-1].age));
scanf("%u", &(list[i-1].wage));
I'm also worried that it is only going to sort the age and not the rest of the information
Yes you are only sorting age, you should sort whole struct based on age.
for(I=0; I<(n-1); I++)
for(j=0; j<n-I-1; j++)
if(list[j].age > list[j+1].age){
tmp = list[j];
list[j] = list[j+1];
list[j+1] = tmp;
}
And the final problem of printing sorted list
printf("Sorted list:\n");
for(I=0; I<n; I++)
printf("%d\n", list[I].age);
PS Note the list[I] in every printf and not list[i+1]
Edit
Instead of sorting it yourself, you can use qsort biult in function in stdlib.h header file.
Implementation: You will need to define a comparator function as you dont have a traditional array to sort
Just define a function
int comparator(const struct employee *p, const struct employee *q)
{
int l = p->age;
int r = q->age;
return (l - r);
}
And then where you sort the list call qsort like this
qsort(list,i,sizeof(list[0]),comparator);

Related

Printing out values in an array not working as expected

I was tasked with inputting student information based on a given struct, where each field of information is to be typed in one line, separated by a space, then the student id is sorted incrementally, and then print out the information, each student on a new line. The problem is while I thought my code was good, the print part keeps giving fractured results and overall just not printing out the correct values. Where should I fix this?
Here's my code:
#include <stdio.h>
typedef struct
{
char id[8];
int year;
}student;
int main() {
student std[100];
int i, j, num, tmp;
printf("So sinh vien:\n");
scanf("%d", &num);
printf("Nhap thong tin sinh vien:\n");
for(i=0; i <= num; i++)
{
scanf("%c %d\n", &std[i].id, &std[i].year);
}
for(i=0; i < num; i++)
{
for (j=1; j< num; j++)
{
if (std[i].id > std[j].id)
{
tmp = *std[i].id
*std[i].id = *std[j].id;
*std[j].id = tmp;
}
}
}
for(i=0; i < num; i++)
{
printf("%c ", std[i].id);
printf("%d\n", std[i].year);
}
return 0;
}
My output is
So sinh vien:
3
Nhap thong tin sinh vien:
12324521 2003
12341552 2002
12357263 2001
Σ 12324521
≡ 3
ⁿ 2341552
Check the return value of scanf() otherwise you may be operating on uninitialized variables.
Check that num less than the 100 records you allocated for student, or even better use a vla along with a check to avoid large input from smashing the stack.
You input num then read num+1 records but later you only print num records.
As you read a character with "%c" the first input with be the \n from the previous scanf().
The struct contains a char id[8] but you only read a single character into it. Read a string instead.
In sort you use > to compare the first letter of id. You probably want to use strcmp() to compare strings.
In sort section you use a int tmp for storing a character of id (which is ok) but then you write an int which is no good.
In sort you only swap the ids. You probably want to swap the entire record not just the ids.
It seems to be an exchange sort. Use a function, and also at least for me the algorithm didn't work as the inner loop variable should start at j=i+1 not 1.
In your print char id[8] as a single char instead of string.
Moved print functionality to a function. This allows you, for instance, to print the students before and after the sort() during debugging.
Minimizing scope of variables (i and j are now loop local, tmp is only used in the swap() function). This makes code easier to reason about.
#include <stdio.h>
#include <string.h>
#define ID_LEN 7
#define str(s) str2(s)
#define str2(s) #s
typedef struct {
char id[ID_LEN+1];
int year;
} student;
void swap(student *a, student *b) {
student tmp = *a;
*a = *b;
*b = tmp;
}
void print(size_t num, student std[num]) {
for(size_t i=0; i < num; i++)
printf("%s %d\n", std[i].id, std[i].year);
}
// exchange sort
void sort(size_t num, student std[num]) {
for(size_t i=0; i < num - 1; i++)
for (size_t j=i+1; j < num ; j++)
if(strcmp(std[i].id, std[j].id) > 0)
swap(&std[i], &std[j]);
}
int main() {
printf("So sinh vien:\n");
size_t num;
if(scanf("%zu", &num) != 1) {
printf("scanf() failed\n)");
return 1;
}
if(num > NUM_MAX) {
printf("Too many students\n");
return 1;
}
student std[num];
printf("Nhap thong tin sinh vien:\n");
for(size_t i=0; i < num; i++)
if(scanf("%" str(ID_LEN) "s %d", std[i].id, &std[i].year) != 2) {
printf("scanf() failed\n");
return 1;
}
sort(num, std);
print(num, std);
}
and here is an example run:
So sinh vien:
3
Nhap thong tin sinh vien:
aaa 1
zzz 2
bbb 3
aaa 1
bbb 3
zzz 2
printf("%c ", std[i].id);
should be
printf("%s ", std[i].id);
%c means a single char.

Issues with printing structs in C

I'm incredibly new to this and have a school assignment I have to write a gradebook program that uses a custom struct to hold student IDs and grades. I have been trying unsuccessfully for days to figure out why it will not print properly, or when it does print (after a lot of shifting things around) it only prints the second set of input.
The gradebook.h section is the custom structure.
// gradebook.h
struct gradeID
{
int id;
char grades[25];
};
// Gradebook.h is a header file to define
// a global structure.
#include "gradebook.h"
#include <stdio.h>
#include <stdlib.h>
void sort(struct gradeID *, int);
int main(void)
{
// Variables and structure definitions
int ctr;
char contInput;
int i;
struct gradeID grade;
struct gradeID *identifier;
int *temps;
// Allocates 10 integers worth of memory for the program to use.
// If there is not enough memory, the program will print an error
// statement and terminate the program.
temps = (int *) malloc(10 * sizeof(int));
if (temps == 0)
{
printf("Not enough memory!\n");
exit(1);
}
// Prints basic instructions for the program
printf("\t\tGradebook Recorder\n");
printf("Input student IDs and grades.\n");
printf("These will be sorted by ID and printed.\n");
/* Creates a for loop that will continue until the program
hits the designated number of array elements. For the sake
of expediency, I have set this amount to 10, but it can be
changed as necessary.*/
for(i = 0; i < 10; i++)
{
printf("Input student ID:\n");
scanf(" %d", &grade.id);
printf("Input grade:\n");
scanf(" %s", grade.grades);
// This allows early exit of the program
printf("Do you have more grades to enter?\n");
printf("Y/N\n");
scanf(" %c", &contInput);
if(contInput == 'N' || contInput == 'n')
{
printf("Finalizing and printing input-\n\n");
break;
}
ctr++;
}
printf("Grades Sorted by Student ID:\n\n");
printf("\tStudent ID: Student Grade: \n");
for(i = 0; i < ctr; i++)
{
printf("\t%d", grade.id );
printf("\t%s", grade.grades);
}
identifier[i] = grade;
return(0);
free(temps);
}
void sort(struct gradeID identifier[], int counter)
{
int inner;
int outer;
struct gradeID temp;
// Loops for sorting
for(outer = 0; outer < counter - 1; ++outer)
{
for(inner = outer + 1; inner < counter; ++inner)
{
if(identifier[inner].id < identifier[outer].id)
{
temp = identifier[inner];
identifier[inner] = identifier[outer];
identifier[outer] = temp;
}
}
}
return;
}
The pointer identifier is uninitialized
struct gradeID *identifier;
So this statement
identifier[i] = grade;
independent on the value of i invokes undefined behavior.
In this for loop
for(i = 0; i < 10; i++)
{
printf("Input student ID:\n");
scanf(" %d", &grade.id);
printf("Input grade:\n");
scanf(" %s", grade.grades);
// This allows early exit of the program
printf("Do you have more grades to enter?\n");
printf("Y/N\n");
scanf(" %c", &contInput);
if(contInput == 'N' || contInput == 'n')
{
printf("Finalizing and printing input-\n\n");
break;
}
ctr++;
}
you are entering new data in the same object grade of the structure type. So the new data overrides the previous data stored in the object.
Moreover the variable ctr was not initialized
int ctr;
So this statement in the above for loop
ctr++;
also invokes undefined behavior.
The variable temps that points to a dynamically allocated array
temps = (int *) malloc(10 * sizeof(int));
is not used.
This statement
free(temps);
never gets the control because before it there is the return statement
return(0);
free(temps);
What you need is to define an array of the type struct gradeID as for example
struct gradeID grades[10];
and fill it with values.

How do I stop my for loop after it iterates through an array and finds the matching element?

I have a for loop that iterates through an array of type struct but when I pass the user input to the function that retrieves the value, it prints out 10 times. I need my main() to ask the user which # of the address book to print so then it is passed to the function that prints it out but the function checks if there is a such number in the array and if there is then it prints out the values of that inputted #.
Here is the code that keeps printing the printAddress() ten times:
STRUCTURE FOR
struct addressBook
{
int entryNum;
char name[100];
char address[100];
};
void printAddress(struct addressBook addresses, int num)
{
int n = 0;
for(n=0; n < 10; n++)
{
if (addresses.checkNum == num)
{
printf("\nBELOW IS ADDRESS # %i DETAILS: \n", num);
printf("Address Book Number: %i \n", addresses.entryNum);
printf("Name: %s", addresses.name);
printf("Address: %s \n", addresses.address);
}
}
}
my main function:
int main()
{
// create a addressBook array of at least 10
struct addressBook addresses[10];
//if statements where user can select to
//add a name to address book, display a single name or quit the program
//this is if they choose to display a single name using a number
else if (choice == 2)
{
int num;
int i = 0;
printf("Enter book number: ");
scanf("%i", &num);
printAddress(addresses[i], num);
}
return 0
}
Here's an adaptation of your code that seems to work as I'd expect it to:
#include <stdio.h>
struct addressBook
{
int entryNum;
char name[100];
char address[100];
};
static
void printAddress(int n_addr, struct addressBook addresses[n_addr], int num)
{
for (int n = 0; n < n_addr; n++)
{
if (addresses[n].entryNum == num)
{
printf("\nBELOW IS ADDRESS # %i DETAILS:\n", num);
printf("Address Book Number: %i\n", addresses[n].entryNum);
printf("Name: %s\n", addresses[n].name);
printf("Address: %s\n", addresses[n].address);
}
}
}
int main(void)
{
struct addressBook addresses[10] =
{
{ 1, "John Doe", "1234 Any St, Some Town, CA 94301" },
{ 2, "Mary Fog", "1270 Some St, Any Town, CA 94303" },
{ 3, "Anne Pit", "1240 Any Rd, Some City, CA 94301" },
{ 4, "Bill Zoo", "1252 Some St, Any Town, CA 94303" },
{ 5, "Phil Tin", "1258 Any Rd, Some City, CA 94301" },
};
int num;
printf("Entry number? ");
scanf("%i", &num);
printAddress(5, addresses, num);
return 0;
}
You could add a return; or break; after the printing in the body of the if in the function. If you add return;, you can add a 'printf("Entry %d not found\n", num);` after the loop and before the function returns.
Note that you don't normally want blanks before newlines; trailing blanks are sloppy. Also, there's a newline after the name now; it looks ugly if you run the name into the tag Address:.
Given the program addr83 compiled from addr83.c, an example run is:
$ addr83
Entry number? 4
BELOW IS ADDRESS # 4 DETAILS:
Address Book Number: 4
Name: Bill Zoo
Address: 1252 Some St, Any Town, CA 94303
$

Proper use of structures and pointers

I have to declare a vector with the "struct" type which, for every n students, it creates a value for the group that student belongs to (which is like a counter), their names and their grades.
The program has to output the name of the students with the highest grade found in these groups. I have to allocate the vector on the heap (I only know the theoretical explanation for heap, but I have no idea how to apply it) and I have to go through the vector using pointers.
For example if I give n the value 4, there will be 4 students and the program will output the maximum grade together with their names as shown here.
This will output Ana 10 and Eva 10.
I gave it a try, but I have no idea how to expand it or fix it so I appreciate all the help I can get with explanations if possible on the practical application of heap and pointers in this type of problem.
#include <stdio.h>
#include <stdlib.h>
struct students {
int group;
char name[20];
int grade;
};
int main()
{
int v[100], n, i;
scanf("%d", n);
for (i = 0; i < n; i++) {
v[i].group = i;
scanf("%s", v[i].name);
scanf("%d", v[i].grade);
}
for (i = 0; i < n; i++) {
printf("%d", v[i].group);
printf("%s", v[i].name);
printf("%d", v[i].grade);
}
return 0;
}
Here I was just trying to create the vector, nothing works though..
It appears, int v[100]; is not quite what you want. Remove that.
You can follow either of two ways.
Use a VLA. After scanning the value of n from user, define the array like struct students v[n]; and carry on.
Define a fixed size array, like struct students v[100];, and use the size to limit the loop conditions.
That said,
scanf("%d", n); should be scanf("%d", &n);, as %d expects a pointer to integer type argument for scanf(). Same goes for other cases, too.
scanf("%s", v[i].name); should better be scanf("%19s", v[i].name); to avoid the possibility of buffer overflow by overly-long inputs.
Even though you are asking for the number of students (groups) using scanf, you hardcoded the upper bound of this value using v[100]. So, I passed your input variable n (the number of students) to malloc in order to allocate the space you need for creating an array of n students.
Also, I used qsort to sort the input array v where the last element would be the max value. Here qsort accepts an array of structs and deference the pointers passed to the comp function to calculate the difference of the comparison.
Finally, I printed the sorted array of structs in the last loop.
#include <stdio.h>
#include <stdlib.h>
struct students {
int group;
char name[20];
int grade;
};
int comp(const void *a, const void *b)
{
return ((((struct students *)a)->grade > ((struct students *)b)->grade) -
(((struct students *)a)->grade < ((struct students *)b)->grade));
}
int main()
{
int n;
printf("Enter number of groups: ");
scanf("%d", &n);
printf("\n");
struct students *v = malloc(n * sizeof(struct students));
int i;
for(i = 0; i < n; i++)
{
v[i].group = i;
printf("\nName: ");
scanf("%s", v[i].name);
printf("Grade: ");
scanf("%d", &v[i].grade);
}
qsort(v, n, sizeof(*v), comp);
for(i = 0; i < n; i++)
{
printf("Group %d, Name %s, grade %d\n", v[i].group, v[i].name, v[i].grade);
}
return (0);
}
You need to replace the standalone array v[100], with an array of structs referencing your structure:
struct students v[100];
However, if you want to use malloc to allocate memory on the heap, you will need to do something like this:
struct students *students = malloc(n * sizeof(struct students));
/* Check void* return pointer from malloc(), just to be safe */
if (students == NULL) {
/* Exit program */
}
and free the requested memory from malloc() at the end, like this:
free(students);
students = NULL;
Additionally, adding to #Sourav Ghosh's answer, it is also good to check the return value of scanf(), especially when dealing with integers.
Instead of simply:
scanf("%d", &n);
A more safe way is this:
if (scanf("%d", &n) != 1) {
/* Exit program */
}
With all this said, your program could look something like this:
#include <stdio.h>
#include <stdlib.h>
#define NAMESTRLEN 20
typedef struct { /* you can use typedef to avoid writing 'struct student' everywhere */
int group;
char name[NAMESTRLEN+1];
int grade;
} student_t;
int
main(void) {
int n, i;
printf("Enter number of students: ");
if (scanf("%d", &n) != 1) {
printf("Invalid input.\n");
exit(EXIT_FAILURE);
}
student_t *students = malloc(n * sizeof(*students));
if (!students) {
printf("Cannot allocate memory for %d structs.\n", n);
exit(EXIT_FAILURE);
}
for (i = 0; i < n; i++) {
students[i].group = i;
printf("Enter student name: ");
scanf("%20s", students[i].name);
printf("Enter students grade: ");
if (scanf("%d", &(students[i].grade)) != 1) {
printf("Invalid grade entered.\n");
exit(EXIT_FAILURE);
}
}
printf("\nStudent Information:\n");
for (i = 0; i < n; i++) {
printf("Group: %d Name: %s Grade: %d\n",
students[i].group,
students[i].name,
students[i].grade);
}
free(students);
students = NULL;
return 0;
}

Searching for a match in a structure array

I've been at this mess for a while and I still haven't figured out where I'm going wrong with it, totally knifing myself if it's something ridiculous like a pointer.
Task shown: Trying to fill a structure array with student ID, name, last name, date of birth, and grades.. Then search by a matching ID that's given to the user.
I'd highly appreciate any help related to this subject, I've been seriously stuck at it for a while. Also I apologize in advance for the french parts
// Part 1
struct Date{
int day;
int month;
int year;
};
// Part 2
struct Student{
int ID;
char name[20];
char lastname[20];
struct Date DOB;
int notes[J];
};
// Part 3
void FillStudentList(struct Student E){
int i;
printf("\nInsert ID: ");
scanf("%d", &E.ID);
printf("Insert name: ");
scanf("%s", &E.name);
printf("Insert last name: ");
scanf("%s", &E.lastname);
printf("Insert date of birth: ");
scanf("%d %d %d", &E.DOB.day, &E.DOB.month, &E.DOB.year);
printf("Insert notes: ");
for(i=0; i<J; i++)
scanf("%d", &E.Notes[i]);
}
// Part 4
void ShowByNb(int Nb, struct Student E[], int NbStudents){
int j, i;
for(i=0; i<NbStudents; i++){
if (E[i].ID== Nb){
printf("\nID: %d", E[i].ID);
printf("\nName: %s", E[i].name);
printf("\nLast Name: %s", E[i].lastname);
printf("\nDate Of Birth: %s-%s-%s", E[i].DOB.day, E[i].DOB.month, E[i].DOB.year);
printf("\nNotes: ");
for(j=0; j<J; j++){
printf("%d", E[i].Notes[j]);
}
}
else
printf("\nInvalid Student!\n");
}
}
// Part 5
void main(){
int i, x;
struct Student E[N];
for(i=0; i<N; i++){
printf("\n\nStudent #%d", i+1);
FillStudentList(E[i]);
}
printf("\n\nSearch student by NB: ");
scanf("%d", &x);
ShowByNb(x, E, N);
}
The edited code below, I believe, achieves your goal. The main problem (other than reading/printing 'int's with '%s' was how you pass your structure to your functions. It is necessary to pass the structure by reference so that its values can be seen outside of the FillStudentList function; see this link.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define N 2
#define J 2
// Part 1
struct Dat{
int jour;
int mois;
int annee;
};
// Part 2
struct Etudiant{
int numero;
char nom[20];
char prenom[20];
struct Dat DDN;
int Notes[J];
};
// Part 3
/* Modified this so that a pointer to the struct is passed instead of a copy of the struct */
void FillStudentList(struct Etudiant *E){
int i;
printf("\nInsert ID: ");
scanf("%d", &E->numero);
printf("Insert name: ");
scanf("%s", E->nom);
printf("Insert last name: ");
scanf("%s", E->prenom);
printf("Insert date of birth: ");
/* These are integers. Do not read with %s */
scanf("%d %d %d", &E->DDN.jour, &E->DDN.mois, &E->DDN.annee);
printf("Insert notes: ");
for(i=0; i<J; i++)
scanf("%d", &E->Notes[i]);
}
// Part 4
void ShowByNb(int Nb, struct Etudiant E[]){
/* Don't redefine N == NbEtudiants making it seem that N is variable */
int j, i;
for(i=0; i<N; i++){
if (E[i].numero == Nb){
printf("\nID: %d", E[i].numero);
printf("\nName: %s", E[i].nom);
printf("\nLast Name: %s", E[i].prenom);
/* Again, can't print integers with %s */
printf("\nDate Of Birth: %d-%d-%d", E[i].DDN.jour, E[i].DDN.mois, E[i].DDN.annee);
printf("\nLes notes: ");
for(j=0; j<J; j++){
printf("%d ", E[i].Notes[j]);
}
return;
}
/* Your previous else would print invalid student every time you ran through the loop even
* if the student number was valid for a later student.
*/
}
/* Only print this if student was not found in any of the N Student structures */
printf("\nInvalid Student!\n");
}
// Part 5
void main(){
setbuf(stdout, NULL);
int i, x;
struct Etudiant E[N];
for(i=0; i<N; i++){
printf("\n\nStudent #%d", i+1);
FillStudentList(&E[i]);
}
printf("\n\nSearch student by NB: ");
scanf("%d", &x);
ShowByNb(x, E);
}
Input
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2
Output
Student #1
Insert ID: 1
Insert name: 1
Insert last name: 1
Insert date of birth: 1
1
1
Insert notes: 1
1
Student #2
Insert ID: 2
Insert name: 2
Insert last name: 2
Insert date of birth: 2
2
2
Insert notes: 2
2
Search student by NB: 2
ID: 2
Name: 2
Last Name: 2
Date Of Birth: 2-2-2
Les notes: 2 2
classic mistake: passing parameter by value instead of by reference:
void FillStudentList(struct Student E){
.....
}
What happens here is that a local copy of your structure is created on the stack, populated with whatever the input is and destroyed when the function exits.
Generally in C, even if you don't want to modify the structure, you pass struct parameters by pointer; if you pass them by value each member of the structure is copied on to the stack...which is a waste of time an memory.
So changing the function prototype (and the code to work with the new signature) should fix the issue:
void FillStudentList(struct Student *E){
....
}

Resources