Sort struct by int value - c

I got an assignment to make program that asks for a number of teams, their names, wins and losses. I have got two problems with the code.
How do I get the team names with spaces in them correctly?
And how to sort the teams by total value (total=wins-losses) before printing?
#include <stdio.h>
#include <string.h>
#define NAME_LENGTH 25
typedef struct {
char name[NAME_LENGTH];
int wins;
int losses;
int total;
} Total;
Total readTeamInfo(void);
void printInfo(Total tt, int);
int main(void) {
int i, teams;
printf("Number of teams > ");
scanf("%d", &teams);
Total data[teams];
for (i = 0; i < teams; i++) {
data[i] = readTeamInfo();
data[i].total = data[i].wins - data[i].losses;
}
for (i = 0; i < teams; i++) {
printInfo(data[i], i);
}
return (0);
}
Total readTeamInfo(void) {
Total tt;
printf("Team name > ");
scanf("%s", tt.name);
printf("Wins > ");
scanf("%d", &tt.wins);
printf("Losses > ");
scanf("%d", &tt.losses);
return (tt);
}
void printInfo(Total tt, int i) {
printf("Team #%d %s: %d wins and %d loses\n", i + 1, tt.name, tt.wins, tt.losses);
}

To read the team name, you can use scanf() with a " %24[^\n]" format. This format specifies a class of characters, namely all characters different from '\n' and the maximum number of characters to store into the destination array before the null terminator. The number 24 is hardcoded here and there is no simple way to specify this number as an expression involving NAME_LENGTH. An alternative solution is to use fgets() to read a full line and strip the newline.
To sort the array of structures, you should use qsort() with a custom comparison function.
Also check the return value of scanf() to detect invalid or missing input and exit with an error message.
It is quite confusing to name the team structure Total. You should use meaningful names such as Team.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NAME_LENGTH 25
typedef struct {
char name[NAME_LENGTH];
int wins;
int losses;
int total;
} Team;
Team readTeamInfo(void);
void printInfo(Team tt, int);
int compare_teams(const void *p1, const void *p2) {
const Team *t1 = p1;
const Team *t2 = p2;
return (t1->total > t2->total) - (t1->total < t2->total);
}
int main() {
int i, teams;
printf("Number of teams > ");
if (scanf("%d", &teams) != 1 || teams <= 0) {
fprintf(stderr, "invalid input\n");
exit(1);
}
Team data[teams];
for (i = 0; i < teams; i++) {
data[i] = readTeamInfo();
data[i].total = data[i].wins - data[i].losses;
}
qsort(data, teams, sizeof(*data), compare_teams);
for (i = 0; i < teams; i++) {
printInfo(data[i], i);
}
return 0;
}
Team readTeamInfo(void) {
Team tt;
printf("Team name > ");
if (scanf(" %24[^\n]", tt.name) != 1) {
fprintf(stderr, "invalid input\n");
exit(1);
}
printf("Wins > ");
if (scanf("%d", &tt.wins) != 1) {
fprintf(stderr, "invalid input\n");
exit(1);
}
printf("Losses > ");
if (scanf("%d", &tt.losses) != 1) {
fprintf(stderr, "invalid input\n");
exit(1);
}
return tt;
}
void printInfo(Team tt, int i) {
printf("Team #%d %s: %d wins and %d loses\n",
i + 1, tt.name, tt.wins, tt.losses);
}

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);
}

C Program gives funny output in VSCode but runs fine in online compiler

I have received a code from a friend of mine which was giving garbage outputs and hence I tried to run it on my PC too. The same problem is happening with me and I don't know how to fix it.
Here is the code to accept and display a few information of bank customers from the user and display it.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
typedef struct Customer {
char name[20];
int date;
int month;
int year;
int amount;
int type_of_account;
} cus;
void display(int n);
void data_entry_of_customers(int n);
int main() {
int i, n, choices;
printf("\n Enter the number of customer you want to enter their data : ");
scanf("%d", &n);
do {
printf("\n Enter your choice :");
scanf("%d", &choices);
switch (choices) {
case 1:
data_entry_of_customers(n);
break;
case 2:
display(n);
break;
case 3:
exit(0);
break;
default:
{
printf("\n Invalid Choice");
}
}
i++;
} while (choices != 3);
getch();
return 0;
}
void data_entry_of_customers(int n) {
cus c[n];
int i;
for (i = 0; i < n; i++) {
printf("\n Enter the name of the customer%d : ", i + 1);
scanf("%s", c[i].name);
printf("\n Enter the date when the account was opened by customer : ");
scanf("%d", &c[i].date);
printf("\n Enter the month when the account was opened by customer : ");
scanf("%d", &c[i].month);
printf("\n Enter the year when the account was opened by customer : ");
scanf("%d", &c[i].year);
printf("\n Enter the amount disposited by the customer : ");
scanf("%d", &c[i].amount);
}
}
void display(int n) {
cus c[n];
int i;
for (i = 0; i < n; i++) {
printf("\n The name of the customer%d is %s", i + 1, c[i].name);
printf("\n The date-month-year when the account was opened by customer%d is %d-%d-%d", i + 1, c[i].date, c[i].month, c[i].year);
printf("\n The amount when the account was opened by customer%d is %d", i + 1, c[i].amount);
printf("\n");
}
}
I tried to look for errors in my code initially but it looked good to me hence I proceeded with an online compiler and now I am clueless on how should I fix the IDE's problem
The c array is defined as a local variable in both the display and the data_entry_of_customers functions. The values entered are lost when the function returns and the contents of c in the display function are indeterminate producing undefined behavior (garbage output).
The program may seem to behave as expected if, by chance, the position and contents of the c array in both functions happens to be the same.
You should more the definition of the array to the main function and pass it to data_entry_of_customers() and display().
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Customer {
char name[20];
int date;
int month;
int year;
int amount;
int type_of_account;
} cus;
void display(int n, cus c[n]);
void data_entry_of_customers(int n, cus c[n]);
int main() {
int n, choices;
printf("\n Enter the number of customers : ");
if (scanf("%d", &n) != 1)
return 1;
cus cus_array[n];
for (;;) {
printf("\n Enter your choice :");
if (scanf("%d", &choices) != 1)
break;
switch (choices) {
case 1:
data_entry_of_customers(n, cus_array);
break;
case 2:
display(n, cus_array);
break;
case 3:
return 0;
default:
printf("\n Invalid Choice\n");
break;
}
}
return 0;
}
void data_entry_of_customers(int n, cus c[n]) {
for (int i = 0; i < n; i++) {
printf("\n Enter the name of the customer%d : ", i + 1);
scanf("%19s", c[i].name);
printf("\n Enter the date when the account was opened by customer : ");
scanf("%d", &c[i].date);
printf("\n Enter the month when the account was opened by customer : ");
scanf("%d", &c[i].month);
printf("\n Enter the year when the account was opened by customer : ");
scanf("%d", &c[i].year);
printf("\n Enter the amount deposited by the customer : ");
scanf("%d", &c[i].amount);
}
}
void display(int n, cus c[n]) {
for (int i = 0; i < n; i++) {
printf("\n The name of the customer%d is %s", i + 1, c[i].name);
printf("\n The date-month-year when the account was opened by customer%d is %d-%d-%d", i + 1, c[i].date, c[i].month, c[i].year);
printf("\n The amount when the account was opened by customer%d is %d", i + 1, c[i].amount);
printf("\n");
}
}
Note these remarks:
void display(int n, cus c[n]); defines a function that receives an int and a pointer to an array of cus structures. The prototype is equivalent to: void display(int n, cus c[]); and void display(int n, cus *c);... A possible improvement is to specify that the cus structures are not modified by the display function, thus change the prototype to void display(int n, const cus *c);
the return value of scanf should be tested to detect and handle invalid input. Writing separate functions to read a string and an integer would help make the code more reliable and readable.
Here is an alternate approach:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Customer {
char name[20];
int date;
int month;
int year;
int amount;
int type_of_account;
} cus;
char *enter_string(const char *prompt, char *dest, size_t size);
int enter_int(const char *prompt, int min, int max);
void display(int n, const cus *c);
void data_entry_of_customers(int n, cus *c);
int main() {
int n = enter_int("Enter the number of customers", 1, 1000);
cus cus_array[n];
for (;;) {
switch (enter_int("Enter your choice", 1, 3)) {
case 1:
data_entry_of_customers(n, cus_array);
break;
case 2:
display(n, cus_array);
break;
case 3:
return 0;
}
}
}
char *enter_string(const char *prompt, char *dest, size_t size) {
int c;
size_t i = 0;
printf("%s: ", prompt);
fflush(stdout);
while ((c = getchar()) != EOF && c != '\n') {
if (i + 1 < size) {
dest[i++] = (char)c;
}
}
printf("\n");
if (i == 0 && c == EOF) {
printf("unexpected end of file\n");
exit(1);
}
if (i < size)
dest[i] = '\0';
return dest;
}
int enter_int(const char *prompt, int min, int max) {
int c, res, value;
for (;;) {
printf("%s: ", prompt);
fflush(stdout);
res = scanf("%d", &value);
while ((c = getchar()) != EOF && c != '\n')
continue;
printf("\n");
if (res == 1) {
if (value >= min && value <= max)
return value;
printf("invalid value, must be between %d and %d\n", min, max);
} else {
if (c == EOF) {
printf("unexpected end of file\n");
exit(1);
}
printf("invalid input, enter a number between %d and %d\n", min, max);
}
}
}
void data_entry_of_customers(int n, cus *c) {
char prompt[80];
for (int i = 0; i < n; i++) {
snprintf(prompt, sizeof prompt, "Enter the name of the customer%d", i + 1);
enter_string(prompt, c[i].name, sizeof(c[i].name));
c[i].date = enter_int("Enter the date when the account was opened by customer",
1, 31);
c[i].month = enter_int("Enter the month when the account was opened by customer",
1, 12);
c[i].year = enter_int("Enter the year when the account was opened by customer",
1900, 2100);
c[i].amount = enter_int("Enter the amount disposited by the customer",
0, 1000000000);
}
}
void display(int n, const cus *c) {
for (int i = 0; i < n; i++) {
printf("The name of the customer%d is %s\n", i + 1, c[i].name);
printf("The date-month-year when the account was opened by customer%d is %d-%d-%d\n",
i + 1, c[i].date, c[i].month, c[i].year);
printf("The amount when the account was opened by customer%d is %d\n",
i + 1, c[i].amount);
printf("\n");
}
}

C program dynamic memory allocation error, string value issue

I have tried multiple methods for a long time but it didn't work, the output automatically updates all previously entered values for the name.
The find method is not required to be checked, but the first display itself provides invalid output:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int phone;
char *name;
} phonecontact;
phonecontact *create(phonecontact *p, int phone, char name[], int i) {
(p+i)->name = (char *)calloc(10, sizeof(char));
(p+i)->name = name;
(p+i)->phone = phone;
printf("\n%s\n", (p+i)->name);
return p;
}
void display(phonecontact *p, int i) {
printf("Name : %s , Phone number : %d\n", p[i].name, p[i].phone);
}
int find(phonecontact *p, char name[], int *num) {
int phone, i = 0;
for (i = 0; i < *num; i++) {
if (strcmp(p[i].name, name) == 1)
display(p, i);
return 1;
}
p = (phonecontact *)realloc(p, ++(*num) * sizeof(phonecontact));
printf("Enter phone number for entered person : ");
scanf("%d", &phone);
p = create(p, phone, name, i + 1);
return 0;
}
int main() {
int num, phone;
char name[30];
printf("Enter number of contacts : ");
scanf("%d", &num);
phonecontact *p;
p = (phonecontact *)calloc(num, sizeof(phonecontact));
for (int i = 0; i < num; i++) {
printf("Enter name : ");
scanf("%s", name);
printf("Enter phone number : ");
scanf("%d", &phone);
p = create(p, phone, name, i);
}
printf("\n%s\n", (p)->name);
printf("\n%s\n", (p + 1)->name);
printf("\n%s\n", (p + 2)->name);
for (int i = 0; i < num; i++) {
display(p, i);
}
printf("Enter name of contact : ");
scanf("%s", &name);
find(p,name, &num);
}
There are many problems in the code:
For each contact to have its own name string, you must allocate a copy of the create() string argument:
phonecontact *create(phonecontact *p, int phone, const char *name, int i) {
p[i].name = strdup(name);
p[i].phone = phone;
printf("\n%s\n", p[i].name);
return p;
}
the function strcmp() returns 0 when the strings compare equal, a negative value if the first argument compares less than the second in lexicographical order and a value greater than zero otherwise.
You should use if (strcmp(p[i].name, name) == 0) and return 1 only if the strings compare equal.
It is confusing for a function named find() to append a new entry in the array.
The array reallocated by find() is not passed back to the caller, and the previous pointer may have become invalid, causing undefined behavior.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int phone;
char *name;
} phonecontact;
int flush_input(void) {
int c;
while ((c = getchar()) != EOF && c != '\n')
continue;
return c;
}
void init_contact(phonecontact *p, int phone, const char *name) {
p->name = strdup(name);
p->phone = phone;
}
void display_contact(const phonecontact *p) {
printf("Name: %s, Phone number: %d\n", p->name, p->phone);
}
int add_contact(phonecontact **pp, int *pnum, const char *name) {
int phone;
int num = *pnum;
phonecontact *p = *pp;
for (int i = 0; i < num; i++) {
if (strcmp(p[i].name, name) == 1) {
display_contact(&p[i]);
return 1;
}
}
/* reallocate the contact array */
p = (phonecontact *)realloc(p, (num + 1) * sizeof(*p));
if (p == NULL) {
printf("cannot reallocate contact array\n");
return -1;
}
printf("Enter phone number for entered person: ");
while (scanf("%d", &phone) != 1) {
printf("Invalid input. Try again: ");
if (flush_input() == EOF)
return -1;
}
init_contact(&p[num], phone, name);
*pnum = num + 1;
*pp = p;
return 0;
}
int main() {
int num, phone;
char name[30];
printf("Enter number of contacts : ");
if (scanf("%d", &num) != 1 || num < 0) {
fprintf(stderr, "invalid input\n");
return 1;
}
phonecontact *p = calloc(num, sizeof(*p));
if (p == NULL) {
fprintf(stderr, "memory allocation error\n");
return 1;
}
for (int i = 0; i < num; i++) {
printf("Enter name: ");
if (scanf(" %29[^\n]", name) != 1) {
fprintf(stderr, "invalid input\n");
return 1;
}
printf("Enter phone number: ");
if (scanf("%d", &phone) != 1)
fprintf(stderr, "invalid input\n");
return 1;
}
init_contact(&p[i], phone, name);
}
for (int i = 0; i < num; i++) {
display_contact(&p[i]);
}
printf("Enter name of contact: ");
if (scanf(" %29[^\n]", &name) == 1)
add_contact(&p, *num, name);
return 0;
}

Can anyone help me identify the error in my last function?

i have written this program (its not finished, there are more functions to be added) however, I want to know why my withinBudget function is sometimes producing correct results and other times producing inaccurate results. The withinBudget works out whether or not the customer has sufficient balance or not. if it returns true it prints purchase has been successful or prints insufficient balance.
#include <stdio.h>
int isItemExist(char itemPrefixes[], char itemPrefix);
void displayMenu(char itemPrefixes[], int itemPrices[], int n);
int withinBudget(int budget, char itemPurchased, char itemPrefixes[], int itemPrices[]);
int n;
int main() {
char itemPrefixes[5];
int itemPrices[5];
int budget = 0;
char itemPurchased;
printf("***ItemPrefixes***");
printf("\nA: Apple\n");
printf("O: Orange\n");
printf("M: Mango\n");
printf("P: Pear\n");
printf("G: Grapes\n");
printf("\n***ShopKeeperPanel***");
printf("\nHow many fruit items do you want to add to the shop?: ");
scanf_s("%d", &n);
int chosenFruitItem = 0;
while (chosenFruitItem < n) {
printf("\n(%d) Enter the item prefix: ", chosenFruitItem + 1);
char itemPrefix = ' ';
scanf_s(" %c", &itemPrefix, 1);
if (isItemExist(itemPrefixes, itemPrefix) == 1) {
printf("Error Item already exist");
continue;
}
else {
itemPrefixes[chosenFruitItem] = itemPrefix;
printf("Enter price for item (%c): ", itemPrefix);
scanf_s("%d", &itemPrices[chosenFruitItem]);
chosenFruitItem++;
}
}
displayMenu(itemPrefixes, itemPrices, n);
printf("\n**CUSTOMER PANEL***");
printf("\nWhat is your budget for today?: ");
scanf_s("%d", &budget);
printf("\nPlease enter Item Prefix from the menu to purchase: ");
scanf_s(" %c", &itemPurchased, 1);
if (withinBudget(budget, itemPurchased, itemPrefixes, itemPrices) == 1) {
printf("PURCHASS SUCCESS");
}
else
{
printf("INSUFFICENT BUDGET");
}
}
int isItemExist(char itemPrefixes[], char itemPrefix) {
for (int i = 0; i < n; i++) {
if (itemPrefixes[i] == itemPrefix) {
return 1;
}
}
return 0;
}
void displayMenu(char itemPrefixes[], int itemPrices[], int n) {
printf("\n*** ShopMenu ***");
printf("\nItem: \t Price: ");
for (int i = 0; i < n; i++) {
printf("\n%c:\t %d", itemPrefixes[i], itemPrices[i]);
}}
int withinBudget(int budget, char itemPurchased, char itemPrefixes[], int itemPrices[]) {
for (int i = 0; i < n; i++) {
if (itemPurchased == itemPrefixes[i] && itemPrices[i] < budget)
{
return 1;
}
return 0;
}
}
the output:
Please ident your code. Either simplest mistakes could go unnoticed because of this.
Here you have an auto one.
After doing this to your code, here's the last function:
int withinBudget(int budget, char itemPurchased, char itemPrefixes[], int itemPrices[]) {
for (int i = 0; i < n; i++) {
if (itemPurchased == itemPrefixes[i] && itemPrices[i] < budget) {
return 1;
}
return 0;
}
}
As you can see, return 0 is inside loop, it's working only if itemPurchased == itemPrefixes[0] && itemPrices[0] < budget as i will never be incremented.
So your function should be:
int withinBudget(int budget, char itemPurchased, char itemPrefixes[], int itemPrices[]) {
for (int i = 0; i < n; i++) {
if (itemPurchased == itemPrefixes[i] && itemPrices[i] < budget) {
return 1;
}
}
return 0;
}

Why I can't input my name with gets?

Here's the part of my code:
Problem: It skips the input of "please enter your name" to "please enter your marks"
What I tried: flushall(); _flushall(); - which worked yesterday somehow, and trying to place these functions between printf,scanf..
student *Create_Class(int size) {
int i, j;
int idStud, nameStud, markStud;
student *classStudent;
classStudent = (student*)malloc(size * sizeof(student));
for (i = 0; i < size; i++) {
classStudent[i].name = (char*)malloc(51 * sizeof(char));
int numOfmarks = 4;
printf("Please enter your name: ");
gets(classStudent[i].name);
_flushall(); //tried _flushall() and it worked yesterday.. tried fflush(NULL) too.
printf("\nPlease enter 4 marks: ");
for (j = 0; j < numOfmarks; j++) {
scanf("%d", &classStudent[i].marks[j]);
}
Avg_Mark(&classStudent[i]);
}
return classStudent;
}
EDIT: (FULL CODE)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
typedef struct {
char *name;
int marks[4];
float avg;
} student;
student *Create_Class(int);
void Avg_Mark(student*);
void Print_One(student*);
void exStudents(student *s, int size);
int main() {
int size, i;
student *arr;
printf("\nEnter the number of students: \n");
scanf("%d", &size);
arr = Create_Class(size);
exStudents(arr, size);
for (i = 0; i < size; i++)
free(arr[i].name);
free(arr);
getch();
}
student *Create_Class(int size) {
int i, j;
int idStud, nameStud, markStud;
student *classStudent;
classStudent = (student*)malloc(size * sizeof(student));
for (i = 0; i < size; i++) {
classStudent[i].name = (char*)malloc(51 * sizeof(char));
int numOfmarks = 4;
int sizeOfName;
printf("Please enter your name: \n");
_flushall();
fgets(classStudent[i].name,50,stdin);
sizeOfName = strlen(classStudent[i].name);
printf("Please enter 4 marks: ");
for (j = 0; j < numOfmarks; j++) {
scanf("%d", &classStudent[i].marks[j]);
}
Avg_Mark(&classStudent[i]);
}
return classStudent;
}
void Avg_Mark(student *s) {
int i, numOfMarks = 4, sum = 0;
for (i = 0; i < numOfMarks; i++) {
sum += s->marks[i];
}
s->avg = (sum / 4.0);
}
void Print_One(student *s) {
printf("The average of %s is %f", s->name, s->avg);
}
void exStudents(student *s, int size) {
int flag = 1;
while (size > 0) {
if (s->avg > 85) {
Print_One(s);
flag = 0;
}
s++;
size--;
}
if (flag)
printf("\n There're no students with above 85 average.");
}
As you have already been told in comments, the solution is to use a two-step approach: Read lines first, then scan these lines as appropriate. This reflects how users are going to answer your prompts, namely by providing the information and then hitting enter.
Here's a variant of your code which does that. I've also changed the main function, because it also used scanf and I've added a function to strip white space from the string input by fgets. (This function requires the <ctype.h> header.)
#include <ctype.h>
int main()
{
char line[80];
int size, i;
puts("Enter the number of students:");
if (fgets(line, sizeof(line), stdin) == NULL) exit(1);
if (sscanf(line, "%d", &size) == 1 && size > 0) {
student *arr = Create_Class(size);
exStudents(arr, size);
for (i = 0; i < size; i++) free(arr[i].name);
free(arr);
}
return 0;
}
/*
* strip white space from beginning and end of string
*/
char *strip(char *str)
{
size_t l = strlen(str);
while (l > 0 && isspace((unsigned char) str[l - 1])) l--;
str[l] = '\0';
while (isspace((unsigned char) *str)) str++;
return str;
}
/*
* Create students and prompt user for input
*/
student *Create_Class(int size)
{
int i;
student *classStudent = malloc(size * sizeof(student));
for (i = 0; i < size; i++) {
char line[80];
char *p;
int okay = 0;
puts("Please enter your name:");
if (fgets(line, sizeof(line), stdin) == NULL) exit(1);
p = strip(line);
classStudent[i].name = malloc(strlen(p) + 1);
strcpy(classStudent[i].name, p);
while (!okay) {
int j = 0;
okay = 1;
puts("Please enter 4 marks:");
if (fgets(line, sizeof(line), stdin) == NULL) exit(1);
p = line;
while (p && j < 4) {
char *tail;
int m = strtol(p, &tail, 10);
if (p == tail) break;
if (m < 1 || m > 100) {
puts("Illegal mark.");
okay = 0;
}
classStudent[i].marks[j++] = m;
p = tail;
}
if (j < 4) {
printf("Expected 4 marks, but got %d.\n", j);
okay = 0;
}
}
Avg_Mark(&classStudent[i]);
}
return classStudent;
}
Please refrain from flushing buffers wihout reason. When a new-line character is written, stdout is flushed, so make it a rule to terminate all your strings with a new-line. New-lines at the beginning of strings instead of at the end are a sign of untidy output.
SOLUTION:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
typedef struct { //struct decleration
char *name;
int marks[4];
float avg;
} student;
//functions decleration
student *Create_Class(int);
void Avg_Mark(student*);
void Print_One(student*);
void exStudents(student *s, int size);
int main() {
/*variable declerations*/
int i, size;
char line[80];
student *arr;
/*Input number of students*/
printf("\nEnter the number of students: \n");
fgets(line, sizeof(line), stdin);
sscanf(line, "%d", &size);
/*Get name of students, marks, and calculate average above 85*/
arr = Create_Class(size);
exStudents(arr, size);
/*Free memory*/
for (i = 0; i < size; i++)
free(arr[i].name);
free(arr);
getch();
}
student *Create_Class(int size) { /*Get names of each student, and their 4 marks.*/
/*Variable declerations*/
int i, j;
char line[51];
student *classStudent;
/*Dynamic allocation to assign structure to every student*/
classStudent = (student*)malloc(size * sizeof(student));
/*Get name of students and their 4 marks*/
for (i = 0; i < size; i++) {
/*Variable decleration and dynamic allocation of 51 chars*/
classStudent[i].name = (char*)malloc(51 * sizeof(char));
int numOfmarks = 4;
int sizeOfName;
/*Input name of student*/
printf("Please enter your name: \n");
scanf("%s", classStudent[i].name);
/*Input marks of student*/
printf("Please enter 4 marks: ");
for (j = 0; j < numOfmarks; j++) {
scanf("%d", &classStudent[i].marks[j]);
}
/*Calculate average, and print averages of students above 85*/
Avg_Mark(&classStudent[i]);
}
return classStudent;
}
/*Calculate averages of students*/
void Avg_Mark(student *s) {
int i, numOfMarks = 4, sum = 0;
for (i = 0; i < numOfMarks; i++) {
sum += s->marks[i];
}
s->avg = (sum / 4.0);
}
/*Print average (if bigger than 85)*/
void Print_One(student *s) {
printf("The average of %s is %0.1f\n", s->name, s->avg);
}
/*Check whether the average is bigger than 85*/
void exStudents(student *s, int size) {
int flag = 1; //flag to check if there are any students with avg above 85
while (size > 0) {
if (s->avg > 85) {
Print_One(s); //Print the average
flag = 0; //We found atleast one student with avg > 85
}
s++; //Advance to next student
size--;
}
if (flag)
printf("\n There're no students with above 85 average.");
}
The problem in your code is that scanf does not consume the new-line returned by the user in:
scanf("%d", &size);
So when the program reaches:
fgets(classStudent[i].name,50,stdin);
the remaining new-line in stdin is received before the user can type anything.
A solution is to replace the initial scanf call by fgets and atoi calls.
char size_str[5];
fgets(size_str,5,stdin);
size = atoi(size_str);
A combination of fgets and sscanf also works also fine in general to first process user inputs and then convert it.
The variant with sscanf is:
char size_str[5];
fgets(size_str,5,stdin);
sscanf(size_str,"%d\n",&size);
Note that it might be safe to stop the program if the value entered is too large. Here we allow from 0 up to 999.
Note also that you have to do the same change some lines below.
instead of:
scanf("%d", &classStudent[i].marks[j]);
write:
char mark_str[5];
fgets(mark_str,5,stdin);
sscanf(mark_str,"%d\n",&classStudent[i].marks[j]);
Hope this helps.

Resources