Crashing issue, might be due to a for loop - c

My program keeps crashing while I run it. I have isolated parts of it (using /**/) to try and figure out what the issue is, and I think it has something to do with the second for loop in my sort function, since isolating that prevents the crashing. However, I've tried fixing it in several different ways (using while/do loops instead, etc), but it manages to keep crashing. Ive also looked at the parameters and how I declared it in main, but I can't see an issue with that. Knowing me, its probably something really dumb ive been missing for hours trying to fix this. Any help would be greatly appreciated
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 5
struct student
{
char name[20];
int hw1, hw2, hw3, ex1, ex2, totalhw, totalex;
float classperc;
char grade;
};
void student_info(struct student s[], int n, int *classex1, int *classex2, int *a, int *b, int *c, int *d, int *f)
{
for (int i = 0; i < n; i++)
{
printf("\n\nPlease enter the student's name:\n");
gets_s(s[i].name, 20);
printf("\nPlease enter the student's homework grades:\n");
scanf("%d %d %d", &(s[i].hw1), &(s[i].hw2), &(s[i].hw3));
printf("\nPlease enter the student's exam scores:\n");
scanf("%d %d", &(s[i].ex1), &(s[i].ex2));
getchar();
s[i].totalhw = s[i].hw1 + s[i].hw2 + s[i].hw3;
s[i].totalex = s[i].ex1 + s[i].ex2;
*classex1 += s[i].ex1;
*classex2 += s[i].ex2;
s[i].classperc = ((float)s[i].totalhw / 1.875) + ((float)s[i].totalex / 3.333);
if (s[i].classperc >= 90)
{
*a = *a + 1;
s[i].grade = 'A';
}
else if (s[i].classperc >= 80)
{
*b = *b + 1;
s[i].grade = 'B';
}
else if (s[i].classperc >= 70)
{
*c = *c + 1;
s[i].grade = 'C';
}
else if (s[i].classperc >= 60)
{
*d = *d + 1;
s[i].grade = 'D';
}
else
{
*f = *f + 1;
s[i].grade = 'F';
}
}
}
void sort(struct student s[], int n)
{
struct student temp;
for (int i = 0; i < SIZE - 1; i++)
{
for (int j = i + 1; j< SIZE; j++)
{
if (strcmp(s[i].name, s[j].name) > 0)
{
temp = s[i];
s[i] = s[j];
s[j] = temp;
}
}
}
for (int i = 0; i < n; i++)
{
printf("\nStudent: %s\nThe Three Homework Scores: %d %d %d\nThe Two Exam Scores: %d %d\n", s[i].name, s[i].hw1, s[i].hw2, s[i].hw3, s[i].ex1, s[i].ex2);
printf("Total Homework Score: %d\nTotal Exam Score: %d\nClass Percentage: %f Grade: %s", s[i].totalhw, s[i].totalex, s[i].classperc, s[i].grade);
// It crashes right before executing this second printf statement (I have no idea why :[)
}
}
void avg_exams(int classex1, int classex2, float *avgex1, float *avgex2)
{
*avgex1 = classex1 / (float)5;
*avgex2 = classex2 / (float)5;
}
void print_classinfo(float avgex1, float avgex2, int a, int b, int c, int d, int f)
{
printf("\n\nThe Average Exam Score for Exam 1 is: %0.2f\nThe Average Exam Score for Exam 2 is: %0.2f\n", avgex1, avgex2);
printf("There were %d A's, %d B's, %d C's, %d D's, %d F's in the class overall\n\n", a, b, c, d, f);
}
void main()
{
struct student s[SIZE];
int a, b, c, d, f , classex1, classex2;
a = b = c = d = f = 0;
classex1 = classex2 = 0;
float classperc, avgex1, avgex2;
student_info( s, SIZE, &classex1, &classex2, &a, &b, &c, &d, &f);
sort(s, SIZE);
avg_exams(classex1, classex2, &avgex1, &avgex2);
print_classinfo(avgex1, avgex2, a, b, c, d, f);
system("PAUSE");
}

Check out your printf() format codes. You can break it into smaller chunks for debugging, to see which portion of the printf() is working unexpectedly.
I don't think the crash is due to your for loops.

Related

C Problem with counting elements in the list of names

I have made one program, where you enter a few characters (10 max). It makes you a list, count average length of surnames, tell about how much different names. But the problem is, when I enter the last number (10) - it sorts me it incorrectly (like 39399349349, 3443993). Beneath I will present my code. I am newbie in C, so please don't shut on me) I am convinced that sorting function is incorrect, but don't know what exactly(
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct people {
int num[10];
char surname[20];
char name[10];
} peoples[10], c;
int compare_people_num(const void *a, const void *b);
int main()
{
int i, j, k = 0, l = 0, m = 0, n = 0;
float s = 0;
char str[100];
system("chcp 1251 > nul");
for (i = 0, j = 0; i < 10; i++, j++)
{
printf("Enter number, surname, name %d of your human: ", i + 1);
fgets(str, sizeof str, stdin);
sscanf(str, "%d %s %s", &peoples[j].num, &peoples[j].name, &peoples[j].name);
while (str[n] != '\n')
{
if (str[n] != ' ')
{
peoples[j].num[k] = str[n];
}
else
break;
n++;
k++;
}
n++;
k = 0;
while (str[n] != '\n')
{
if (str[n] != ' ')
{
peoples[j].surname[k] = str[n];
}
else
break;
n++;
k++;
}
n++;
k = 0;
while (str[n] != '\n')
{
if (str[n] != '\0')
{
peoples[j].name[k] = str[n];
}
else
break;
n++;
k++;
}
n = 0;
k = 0;
}
for (i = 0; i < 10; i++)
{
for (j = i + 1; j < 10; j++)
{
if (!strcmp(peoples[i].name, peoples[j].name))
m = 1;
}
if (m == 0)
l++;
m = 0;
s = s + strlen(peoples[i].surname);
}
for (i = 0; i < 9; i++)
for (j = 0; j < 9; j++)
if (strcmp(peoples[j].num, peoples[j+1].num) > 0)
{
qsort(peoples, 10, sizeof(struct people), &compare_people_num);
}
for (i = 0; i < 10; i++)
{
printf("%d ", peoples[i].num);
printf("%s ", peoples[i].name);
printf("%s ", peoples[i].surname);
printf("\n");
}
printf("\nYou have %d different names\n", l);
printf("Avarege lenght of surname is = %f\n", s / 10);
}
int compare_people_num(const void *a, const void *b)
{
const struct people *p1 = a;
const struct people *p2 = b;
return p1->num - p2->num; // Change order to reverse sort
}
I went through your code and removed things that weren't needed. In both your input and sorting, it seemed like you were doing things twice. I tried to document the changes I made and explain why they should be made.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// It's generally a good idea to use #define when you have some global constants
// I made some of the constants larger than what you showed to prevent issues
#define MAX_NAME_LEN 40
#define MAX_SURNAME_LEN 40
#define NUM_PEOPLE 10
#define BUFF_LEN 100
// Separate your struct...
struct person {
int num;
char name[MAX_NAME_LEN];
char surname [MAX_SURNAME_LEN];
};
// ... and array decleration
static struct person people[NUM_PEOPLE];
// I added this function, to make it easier to display a person
void print_person (const struct person * p) {
printf("Person %d %s %s\n", p->num, p->name, p->surname);
}
// This function will print out every person in the people array
void print_people (void) {
for (int i=0; i<NUM_PEOPLE; i++) {
print_person(&people[i]);
}
}
// Compares two people by number
int compare_people_num (const void * a, const void * b) {
struct person * p0 = (struct person *) a;
struct person * p1 = (struct person *) b;
return p0->num - p1->num;
}
// Compares two people by name
int compare_people_name (const void * a, const void * b) {
struct person * p0 = (struct person *) a;
struct person * p1 = (struct person *) b;
return strcmp(p0->name, p1->name);
}
int main (void) {
int i;
char buffer[BUFF_LEN];
for (i=0; i<NUM_PEOPLE; i++) {
printf("Enter number, surname, and name of person %d: ", i+1);
fflush(stdout); // fflush makes sure that our text is shown to the user
fgets(buffer, BUFF_LEN, stdin); // Read user input in to buffer
// It's unclear what you were doing here
// This sscanf line takes a line of text, and splits it into a number and two words
// It then stores that number in people[i].num, and stores the words in name and surname
// However, right after this, you have several while loops that appear to be manually doing the same
// thing all over again. If you want to read all of the input in, just the line below is enough
sscanf(buffer, "%d %s %s", &people[i].num, people[i].name, people[i].surname);
}
// We've read all of the people in now
// Uncomment the next line to check out the output at this state:
// print_people();
// To count names, we first need to sort the people by their name
// We do this using a qsort call
qsort(people, NUM_PEOPLE, sizeof(struct person), compare_people_name);
// Once the names are sorted, we'll calculate how many different names there are
// We start the count at 1, and start checking from the second person (index 1)
// This is because the first person will always be unqiue, and we can't compare to
// person negative 1
int n_names = 1;
for (i=1; i<NUM_PEOPLE; i++) {
char * current = people[i].name;
char * previous = people[i-1].name;
if (!strcmp(current, previous)) {
n_names ++;
}
}
// Now we have to sort the people based on their num field
// Again, in your code, it looked like you were doing this more than nessecary
// We just have to call qsort once, as such
qsort(people, NUM_PEOPLE, sizeof(struct person), compare_people_num);
// We will also do a loop through to calculate the average surname length
float avg_surname_len = 0;
for (i=0; i<NUM_PEOPLE; i++) {
avg_surname_len += (float)strlen(people[i].surname);
}
avg_surname_len /= (float)NUM_PEOPLE;
// We're all done! The people are sorted by number.
print_people();
printf("There are %d unique names\n", n_names);
printf("The average surnames is %f characters\n", avg_surname_len);
}

Factorial calculator using functions in C

I am learning about functions and how to call upon them and use them in class. I don't quite understand where I've gone wrong here. I know that there are some mistakes around the int main part. I have asked my teacher and he is reluctant on giving me an example that would solve my problems or help me out. I think my main problem is at factorial_result = factorial();
#include <stdio.h>
void mystamp(void)
{
printf("My name is John Appleseed\n");
printf("My lab time is 12:30 on Sunday\n");
return;
}
int getnum(void)
{
int local_var;
printf("Please enter an integer: ");
scanf("%d%*c", local_var);
return(local_var);
}
int factorial(void)
{
int x,f=1,local_var;
for(x=1; x <= local_var; x++)
f = f * x;
return(f);
}
int main(void)
{
int result;
int factorial_result;
mystamp();
result = getnum();
factorial_result = factorial();
printf("You typed %d\n", result);
printf("The factorial is %d\n", factorial_result);
return;
}
Declare local_var as a global variable and do:
local_var = getnum();
OR
Change main() to:
int main(void)
{
int result;
int factorial_result;
mystamp();
result = getnum();
factorial_result = factorial(result);
printf("You typed %d\n", result);
printf("The factorial is %d\n", factorial_result);
return;
}
And factorial() to:
int factorial(int n)
{
int x,f=1,local_var=n;
for(x=1; x <= local_var; x++)
f = f * x;
return(f);
}
Your factorial should be calculated based on the input( i.e in your case int result ).
So, your method factorial() should looks as follows :
int factorial( int number )
{
int factorial_value = 1;
while( number > 0 )
{
factorial_value *= number;
number--;
}
return factorial_value;
}
Then, the correct factorial would be returned and printed accordingly ! Regarding the scope of the variables that you have used, see the comments under your question.
#include <stdio.h>
int factorial(int);
int main()
{
int num;
int result;
printf("Enter a number to find it's Factorial: ");
scanf("%d", &num);
if (num < 0)
{
printf("Factorial of negative number not possible\n");
}
else
{
result = factorial(num);
printf("The Factorial of %d is %d.\n", num, result);
}
return 0;
}
int factorial(int num)
{
if (num == 0 || num == 1)
{
return 1;
}
else
{
return(num * factorial(num - 1));
}
}
This is a simple factorial program using recursion calling function !
include
int main()
{
int c, n, fact = 1;
printf("Enter a number to calculate its factorial\n"); scanf("%d", &n);
for (c = 1; c <= n; c++) fact = fact * c;
printf("Factorial of %d = %d\n", n, fact);
return 0;
}

Union Find - Quick Find

I just started the Princeton Algorithms course and tried to implement a very basic quick find algorithm in C as follows -
#include <stdio.h>
void find(int *, int, int);
void unite(int *, int, int);
int main() {
int arr[10], i, n1, n2, opt;
char ch;
for (i = 0; i < 10; i++)
arr[i] = i;
do {
printf("1. Find\n2. Union\n");
scanf("%d", &opt);
if (opt == 1) {
scanf("%d,%d", &n1, &n2);
find(arr, n1, n2);
}
if (opt == 2) {
scanf("%d,%d", &n1, &n2);
unite(arr, n1, n2);
}
for (i = 0; i < 10; i++)
printf("%d ", arr[i]);
printf("Continue? (Y/N)");
getchar();
scanf("%c", &ch);
} while (ch == 'Y');
}
void find(int *id, int p, int q) {
if ((*(id + p)) == (*(id + q)))
printf("Connected\n");
}
void unite(int *id, int p, int q) {
int i;
for (i = 0; i < 10; i++) {
if ((*(id + i)) == (*(id + p)))
*(id + i) = *(id + q);
}
}
The program isn't running as it is supposed to. When I try to do a union(4,3) and then a union(3,8), only arr[3] changes its value and not arr[4]. Also, I'm not sure why I had to use getchar (the program kept ending without it).
This line:
if((*(id+i))==(*(id+p)))
is equivalent to:
if (id[i] == id[p])
and tests the current id for i with the current id for p.
The problem is that id[p] may have already been changed to id[q]!
So when you try to turn all 3's into 8's, after we change arr[3] to 8, from then on we are only changing 8's into 8's.
Instead try storing the current id for p, and testing against that:
void unite(int *id, int p, int q)
{
int i;
int id_to_change = id[p];
for(i=0;i<10;i++)
{
if(id[i] == id_to_change)
id[i] = id[q];
}
}

scanf not passing value to variable

I wanted to ask a little bit about scanf in C using Xcode IDE. If I not initially set value for variable choice, anytime I open my program and enter any choice(either 1/2) it will go to else case every time. So I check the value after select any choice then I got a strange number. Could you please take a look at my code. Thank you in advance.
Here's my actual code:
/* Bubble Sort using MPI */
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#include <time.h>
#define N 1000
double startT,stopT;
double startTime;
void showElapsed(int id, char *m)
{
printf("%d: %s %f secs\n",id,m,(clock()-startTime)/CLOCKS_PER_SEC);
}
void showVector(int *v, int n, int id)
{
int i;
printf("%d: ",id);
for(i=0;i<n;i++)
printf("%d ",v[i]);
putchar('\n');
}
int * merge(int *v1, int n1, int *v2, int n2)
{
int i,j,k;
int * result;
result = (int *)malloc((n1+n2)*sizeof(int));
/*
i : pointer of v1
j : pointer of v2
k : pointer of k
*/
i=0; j=0; k=0;
while(i<n1 && j<n2)
if(v1[i]<v2[j])
{
result[k] = v1[i];
i++; k++;
}
else
{
result[k] = v2[j];
j++; k++;
}
if(i==n1)
while(j<n2)
{
result[k] = v2[j];
j++; k++;
}
else
while(i<n1)
{
result[k] = v1[i];
i++; k++;
}
return result;
}
void swap(int *v, int i, int j)
{
int t;
t = v[i];
v[i] = v[j];
v[j] = t;
}
void sort(int *v, int n)
{
int i,j;
for(i=n-2;i>=0;i--)
for(j=0;j<=i;j++)
if(v[j]>v[j+1])
swap(v,j,j+1);
}
int main(int argc, char **argv)
{
int * data;
int * chunk;
int * other;
int m,n=N;
int id,p;
int s;
int i;
int step;
int choice = 0;
//start asking user to select option between sequential or parallel version of BubbleSort
printf(":: Welcome to BubbleSort Project for CSS333 ::\n");
printf("Please select option that you prefer\n");
printf("Type \"1\" for sequential mode or \"2\" for parallel mode\n");
printf("");
fflush(stdout);
scanf("Enter here: %d", &choice);
printf("Test value of choice(should be either 1 or 2): %d\n", choice);
//end asking
if(choice == 1){
// do seq
printf("You have selected option 1 which is running BubbleSort in Sequential mode\n");
printf("Please wait...");
}
else if(choice == 2){
// do parallel
printf("You have selected option 2 which is running BubbleSort in parallel mode\n");
printf("Please wait...");
MPI_Status status;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&id);
MPI_Comm_size(MPI_COMM_WORLD,&p);
if(id==0)
{
int r;
srandom(clock());
s = n/p;
r = n%p;
data = (int *)malloc((n+p-r)*sizeof(int));
for(i=0;i<n;i++)
data[i] = random();
if(r!=0)
{
for(i=n;i<n+p-r;i++)
data[i]=0;
s=s+1;
}
startT = clock();
MPI_Bcast(&s,1,MPI_INT,0,MPI_COMM_WORLD);
chunk = (int *)malloc(s*sizeof(int));
MPI_Scatter(data,s,MPI_INT,chunk,s,MPI_INT,0,MPI_COMM_WORLD);
sort(chunk,s);
}
else
{
MPI_Bcast(&s,1,MPI_INT,0,MPI_COMM_WORLD);
chunk = (int *)malloc(s*sizeof(int));
MPI_Scatter(&data,s,MPI_INT,chunk,s,MPI_INT,0,MPI_COMM_WORLD);
sort(chunk,s);
}
step = 1;
while(step<p)
{
if(id%(2*step)==0)
{
if(id+step<p)
{
MPI_Recv(&m,1,MPI_INT,id+step,0,MPI_COMM_WORLD,&status);
other = (int *)malloc(m*sizeof(int));
MPI_Recv(other,m,MPI_INT,id+step,0,MPI_COMM_WORLD,&status);
chunk = merge(chunk,s,other,m);
s = s+m;
}
}
else
{
int near = id-step;
MPI_Send(&s,1,MPI_INT,near,0,MPI_COMM_WORLD);
MPI_Send(chunk,s,MPI_INT,near,0,MPI_COMM_WORLD);
break;
}
step = step*2;
}
if(id==0)
{
FILE * fout;
stopT = clock();
printf("%d; %d processors; %f secs\n",N,p,(stopT-startT)/CLOCKS_PER_SEC);
fout = fopen("result","w");
for(i=0;i<s;i++)
if (chunk[i] != 0)
fprintf(fout,"%d\n",chunk[i]);
fclose(fout);
}
MPI_Finalize();
}
else{
printf("Invalid value\n");
printf("Program exiting...\n");
exit(0);
}
}
This is your problem:
scanf("Enter here: %d", &choice);
You might be expecting this to display "Enter here: " then accept a number as input and store it in the variable choice. But that's not what it does.
What this does is that it goes through the formatting string ("Enter here: %d"), one character by one. For each character that is not '%', it reads a character from stdin and compares them together. If they don't match, it pushes the character back to the buffer of stdin and stops scanning.
So unless the user types in something starting with Enter here: followed immediately by a number, it fails at reading that number.
What you probably wanted to do is to:
printf("Enter here: ");
scanf("%d", &choice);
(and then read the documentation for scanf().

I need help regarding the function to show the top 5 scores in text file in C

I am currently creating a really simple program for my beginners course in C programming and I am stuck. I want to be able to print the 5 best scores that are saved in a text file but right now it is only showing the first 5 lines not the best scored lines. The history part works okay and is printing everything but I cannot understand how to print the 5 best scores, maybe it has to do with sorting out the information in the extern text file? Sorry if this post is dumb but I need to ask somewhere (I am really basic at this).
Here is a part of the code that I think is not working properly, it is for codeblocks:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
char name [40]; // Here every integer and character etc. is introduced as global.
int menu(char *name);
int startPractice(int choice);
int startTest(int choice, char *name);
char randomOperator();
int getRandomNumber(int min, int max);
int calc(int a, int b, char c);
int rand(void);
int showHistory();
int saveHistory(char *name, float average);
int showTop5();
struct gamer{
char *username;
float average;
};
int main()
{
printf("Hello and welcome to the program!\n"); // This is the introduction
printf("\nWhat is your name? ");
scanf("%s", &name);
printf("\nWelcome %s! \n\nWhat would you like to do? \n" , name);
menu(&name); // This line calls the main menu.
return 0;
}
int compare(const void *s1, const void *s2)
{
struct gamer *g1 = (struct gamer *)s1;
struct gamer *g2 = (struct gamer *)s2;/* same gender so sort by id */
return g1->average - g2->average;
}
int menu(char *name) { // This is the main menu and whenever this function is called the program starts from here.
int qanswer; // This variable is set so it can be used in this body, it is not a global integer.
printf("\n1. Do practices \n2. Do a test \n3. Quit \n4. Test history \n5. Top 5 \n");
scanf("%d", &qanswer);
switch (qanswer) { // Switch cases for a more compressed code, here only one case will be activated and that is decided by the value of "qanswer".
case 1:
printf("\nNow, you can choose to do practices on: \n\n1. Additions\n2. Subtractions\n3. Addition and Subtractions: ");
printf("\n\nEnter your choice: ");
scanf("%d" , &qanswer);
startPractice(qanswer);
break; //This breaks the code at any chosen case.
case 2:
printf("\nYou will be given 15 questions to solve as the test!\n\n");
printf("\nYou can choose to do the test on: \n\n1. Additions\n2. Subtractions\n3. Addition and Subtractions: ");
printf("\n\nEnter your choice: ");
scanf("%d", &qanswer);
startTest(qanswer, name);
break;
case 3:
printf("\nExiting program... \n");
exit(0); // Shuts down the program.
break;
case 4:
printf("Test history\n");
showHistory();
break;
case 5:
printf("Top 5 test results:\n");
showTop5();
break;
default: // if the user enter anything except 1,2,3 this line of code will be returned.
printf("Your input wasn't valid. Please try again...\n\n");
menu(name); // This calls the main menu again
}
return 0;
}
int startPractice(int choice) { // If practice is chosen then this function will be called.
int a, b, answer1;
char c;
int i;
for (i = 1; i <= 10; i++) { // The for loop runs this code until i=10 and increases by 1 each time. Therefore this code will run 10 times fully.
a = getRandomNumber(1, 30); // This calls the function for a random number.
b = getRandomNumber(1, 30);
if (choice == 1) { // If choice is set to "1" c will be t to + otherwise -.
c = '+';
} else if (choice == 2) {
c = '-';
} else {
c = randomOperator();
}
printf("%d. %d %c %d = ", i, a, c, b);
scanf("%d", &answer1);
while (answer1 != calc(a, b, c)){
printf("Try again! ");
scanf("%d", &answer1);
}
printf("Very good!\n\n");
}
printf("Practice is complete\n");
printf("\nNow what would you like to do?\n");
menu(name);
return 0;
}
int startTest(int choice, char *name) {
int a, b, answer1;
char c;
int counter = 0;
float average;
int i;
for (i = 1; i <= 15; i++) {
a = getRandomNumber(1, 30);
b = getRandomNumber(1, 30);
if (choice == 1) {
c = '+';
} else if (choice == 2) {
c = '-';
} else {
c = randomOperator();
}
printf("%d. %d %c %d = ", i, a, c, b);
scanf("%d", &answer1);
if (answer1 == calc(a, b, c)){
counter++;
}
printf("\n\n");
}
printf("The test is complete\n");
average = (float) counter/(float)15; // This calculates the average score as a float with the counter / 15.
printf("You scored %d out of 15 which is and average of %d %%\n", counter, (int)average*100); // This line takes the value of average and multiply it by 100 to show the percentage.
saveHistory("Test", average);
printf("\nNow what would you like to do?\n");
menu(name); // This function calls the main menu again.
return 0;
}
int calc(int a, int b, char c) { // This function is used to define the equation as (a + b) or (a - b).
switch(c) {
case '+':
return a+b;
case '-':
return a-b;
}
}
char randomOperator() { //This code is used to decide if + or - is going to be used.
switch(getRandomNumber(0,1)) { //Switch statement when you need two or more ways to return a value.
case 0:
return '+'; //If the random number is 0 return is + otherwise it is -.
case 1:
return '-';
}
return '+';
}
int getRandomNumber(int min, int max) { // This simply decides a random value of min 0 and max 1.
return rand() % (max + 1 - min) + min;
}
int showHistory(){
FILE *f;
char c;
f=fopen("test.txt","rt");
if (f) {
while((c=fgetc(f))!=EOF){
printf("%c",c);
}
fclose(f);
printf("\nWhat would you like to do now?\n");
} else {
printf("There is no history file at the moment.");
}
menu(name);
return 0;
}
int saveHistory(char *name, float average){
FILE *f;
char c;
f=fopen("test.txt","a");
fprintf(f, "%s_%.2f\n", name, average);
fclose(f);
return 0;
}
int showTop5(){
//printf("\n");
FILE *f;
char c;
char line[256];
int count = 0;
f=fopen("test.txt","rt");
if (f) {
while (fgets(line, sizeof(line), f)) {
if (strlen(line) > 0) {
count++;
}
}
char delimiter[] = "_";
char *ptr;
fclose(f);
f=fopen("test.txt","rt");
struct gamer gamers[count];
int i = 0;
printf("\n");
while (fgets(line, sizeof(line), f)) {
ptr = strtok(line, delimiter);
char n[40];
strcpy(n, ptr);
//printf("n: %s, ", n);
ptr = strtok(NULL, delimiter);
float avg = *(float *) ptr;
//printf("points: %f\n", avg);
gamers[i].average = avg;
gamers[i].username = n;
i++;
}
qsort(gamers, count, sizeof(struct gamer), compare);
int j;
for(j = 0; j < count; j++){
if (j==5){
break;
}
printf("Rank %d: %s with %.2f %% as average.\n", j+1, gamers[j].username, gamers[j]. average);
}
fclose(f);
} else {
printf("There is no history file at the moment.");
}
menu(name);
return 0;
}
This isnt going to work
ptr = strtok(NULL, delimiter);
float avg = *(float *) ptr;
you are treating the score in the file as though it were the binary representation of a float. It is not, it is text. You need to do
ptr = strtok(NULL, delimiter);
float avg = atof(ptr);
So when i fixed the code that made the program crash (as was mentioned in the answers, thanks!) it seems like the problem with the sorting came from this line of code not treating the values correct, since I wanted to return it as percentage and it must have stopped reading after the decimal which made every user either scoring 0 or 1 if this makes sense. It works now when the return value is multiplied by 100.
Started with this code:
int compare(const void *s1, const void *s2)
{
struct gamer *g1 = (struct gamer *)s1;
struct gamer *g2 = (struct gamer *)s2;/* same gender so sort by id */
return g1->average - g2->average;
}
And this code seems to be the problemsolver.
int compare(const void *s1, const void *s2)
{
struct Gamer *g1 = (struct Gamer *)s1;
struct Gamer *g2 = (struct Gamer *)s2;/* same gender so sort by id */
return (int)((g2->average - g1->average)*100);
After taking into account of previous remarks, it remains other problems before to manage correctly the sort of array of struct gamer by using the qsort() optimized function.
Problem 1 - no allocated space to store the username into a struct gamer.
The struct gamer is using a char *username;;
When reading data from "test.txt" file, the user name is storing only the pointer to the buffer instead of the content gamers[i]->username = n;;
Even if the read username is strcpy(n, ptr);, that storage is in the heap and be overwritten at the next reading operation;
As #pm100 suggestes, the average is decoded as a binary data instead of a text value float avg = *(float *) ptr;
In the showTop5() function use:
while (fgets(line, sizeof(line), f)) {
ptr = strtok(line, delimiter);
char n[40];
strcpy(n, ptr);
ptr = strtok(NULL, delimiter);
float avg = atof(ptr); // #pm100 proposed solution
gamers[i].average = avg;
gamers[i].username = (char *)malloc(sizeof(char)*(strlen(n)+1));
strcpy(gamers[i].username,n); // allocate and copy the username
i++;
}
Instead of
while (fgets(line, sizeof(line), f)) {
ptr = strtok(line, delimiter);
char n[40];
strcpy(n, ptr);
//printf("n: %s, ", n);
ptr = strtok(NULL, delimiter);
float avg = *(float *) ptr;
//printf("points: %f\n", avg);
gamers[i].average = avg;
gamers[i].username = n; // error no size allocated
i++;
}
Problem 2 - When using qsort() the structure of data shall have a fixed-size length. Use an array of pointer struct gamer * to be sorted.
If sizeof(struct gamer) is used to qsort(), the float average and the char *username will be sorted, but it is not optimized;
After allocating the size to store username, it will be not easy to free the memory if the array struct gamer gamers[count]; is allocated from the heap;
It the amount of gamers is large, storing in the heap could be a problem;
Allocating the gamers[] array:
struct gamer **gamers;
int i;
gamers = malloc(count*sizeof(struct gamer *));
for (i = 0; i < count; i++) {
gamers[i] = malloc(sizeof(struct gamer));
}
Manage the reading operation to the array:
See the source code in the Problem 1
Update the compare function of quicksort:
int compare(const void *s1, const void *s2)
{
struct gamer *g1 = *((struct gamer **)s1); // instead of (struct gamer *)s1;
struct gamer *g2 = *((struct gamer **)s2); // instead of (struct gamer *)s2;
// as #BeginnerC suggests, reverse the comparison
return ((int)((g2->average - g1->average)*100));
}
Perform the quicksort:
qsort(gamers, count, sizeof(struct gamer *), compare);
Print the Top 5 gamers:
for(j = 0; j < count; j++){
if (j==5){
break;
}
printf("Rank %d: %s with %.2f %% as average.\n", j+1,
gamers[j]->username, gamers[j]->average);
}
Free the allocated gamers[] array:
for (i = 0; i < count; i++) {
free (gamers[i]->username); // allocated to the username
free (gamers[i]);
}
free(gamers);
Note: in order to simplify the solution of Problem 1 and to have a fixed-size struct gamer, the field char *username could be replaced by char username[40]; (because char n[40]; before strcpy(n, ptr);.

Resources