C with structures, pointers and malloc - c

I am trying to write a c program that uses malloc for a struct, then uses another function to scan the struct. Afterward, the alphanumeric values of the struct use malloc as well. In the end, it calculated the average of the float values of the struct.
#include <stdio.h>
#include <stdlib.h>
#define MAX_ARRAY 20
#define MAX_STRUCT_ARRAY 30
#define MIN_STRUCT_ARRAY 0
#define NULL_CHAR '/0'
#define NULL_NUM 0
#define MAX_MARKS 10
struct students
{ char name[MAX_ARRAY];
int id;
float marks;
};
typedef struct students studentsDef;
void scanStruct(int numofStructs,struct students listOfstudents);
float mesosOros(int numofStructs,float grades[MAX_STRUCT_ARRAY]);
int main()
{
int structNum;
studentsDef* pStudents = NULL;
do{
printf("Dwse ton arithmo ton mathiton: ");
scanf("&d",&structNum);
if(structNum > MAX_STRUCT_ARRAY || structNum < MIN_STRUCT_ARRAY)
{
printf("Oriste arithmo domwn apo %d ews %d. \n",MIN_STRUCT_ARRAY,MAX_STRUCT_ARRAY);
}
}while(structNum > MAX_STRUCT_ARRAY || structNum < MIN_STRUCT_ARRAY);
pStudents=(studentsDef*)malloc(structNum * sizeof(studentsDef));
if(pStudents==NULL)
{
printf("Adunati i dunamiki desmefsi mnimis. \n");
}
else
{
scanStruct(structNum,*pStudents);
mesosOros(structNum,*pStudents.marks);
}
return 0;
}
void scanStruct(int numofStructs,struct students listOfstudents)
{
int i,j,sizeOfChar;
for(i=0;i < numofStructs;i++)
{
do{
printf("Dwse plithos haraktirwn tou onomatos: ");
scanf("%d",&sizeOfChar);
if((sizeOfChar<MIN_STRUCT_ARRAY) || (sizeOfChar>MAX_ARRAY))
{
printf("Apodekti eisagwgi haraktirwn ews %d.\n",MAX_ARRAY);
}
}while((sizeOfChar<MIN_STRUCT_ARRAY) || (sizeOfChar>MAX_ARRAY));
// listOfstudents[i].name = (char*)malloc((sizeOfChar + 1) * sizeof(char));
printf("Onoma mathiti: ");
scanf("%s", listOfstudents[i].name);
do{
printf("dwse to id tou mathiti: ");
scanf("%d",&listOfstudents[i].id);
if((listOfstudents[i].id < NULL_NUM) || (listOfstudents[i].id == NULL_NUM))
{
printf("Adunatio gia arnitiko i mideniko id.\n");
}
}while((listOfstudents[i].id < NULL_NUM) || (listOfstudents[i].id == NULL_NUM));
do{
printf("dwse bathmo: ");
scanf("%f",&listOfstudents[i].marks);
if((listOfstudents[i].marks < NULL_NUM) || (listOfstudents[i].marks == NULL_NUM) || listOfstudents[i].marks > MAX_MARKS)
{
printf("Adunatio gia arnitiko i mideniko id");
}
}while((listOfstudents[i].marks < NULL_NUM) || (listOfstudents[i].marks == NULL_NUM) || listOfstudents[i].marks > MAX_MARKS);
}
}
float mesosOros(int numofStructs,float grades[MAX_STRUCT_ARRAY])
{
int i;
for(i=0; i<numofStructs;i++)
{
grades[i]+=0;
}
return grades[i]/numofStructs;
}
So far I am unable to take advantage of malloc in the second function and also can't pass the struct through the function calls. Thanks in advance! Sorry for any eyesores I am new to structures.

Try this code, it may get you started.
Further below is a patch file you can use to change the code sample into newmain.c and it quickly explains each change. Not all are significant to your question and not all will produce results required. Hoping that it helps, a little bit.
Good luck!
main < input is one way to redirect input from the file input to the program main, the symbol < is used for redirection. The output from this specific run is shown too.
gcc -o main main.c
main < input
Dwse ton arithmo ton mathiton: structNum: 1
Dwse plithos haraktirwn tou onomatos: sizeOfChar: 10
10
Onoma mathiti: listOfstudents[i].name: ABCD EFGH
dwse to id tou mathiti: listOfstudents[i].id: 1
dwse bathmo: listOfstudents[i].marks: 9.000000
RES: 9.000000
Here is the file input, contents. I choose the format, your format needs may be different, CSV, space separated values, and so on. Hope this is close enough to be helpful.
1
10
ABCD EFGH
1
9.0
This is the code, it produces the output shown above when the input is redirected to stdin for the scanf function to read data into the malloc memory.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_ARRAY 20
#define MAX_STRUCT_ARRAY 30
#define MIN_STRUCT_ARRAY 0
#define NULL_CHAR '/0'
#define NULL_NUM 0
#define MAX_MARKS 10
struct students
{ char name[MAX_ARRAY];
int id;
float marks;
};
typedef struct students studentsDef;
typedef struct students* studentsDefPTR;
void scanStruct(int numofStructs, studentsDefPTR listOfstudents);
float mesosOros(int numofStructs, studentsDefPTR listOfstudents);
int main()
{
int structNum;
studentsDefPTR pStudents = NULL;
do{
printf("Dwse ton arithmo ton mathiton: ");
scanf("%d%*c",&structNum);
if(structNum > MAX_STRUCT_ARRAY || structNum < MIN_STRUCT_ARRAY)
{
printf("Oriste arithmo domwn apo %d ews %d. \n",MIN_STRUCT_ARRAY,MAX_STRUCT_ARRAY);
}
}while(structNum > MAX_STRUCT_ARRAY || structNum < MIN_STRUCT_ARRAY);
printf("structNum: %d\n",structNum);
pStudents=(studentsDefPTR)malloc(structNum * sizeof(studentsDef));
if(pStudents==NULL)
{
printf("Adunati i dunamiki desmefsi mnimis. \n");
}
else
{
scanStruct(structNum,pStudents);
printf( "RES: %f\n", mesosOros(structNum,pStudents));
}
return 0;
}
void scanStruct(int numofStructs,studentsDefPTR listOfstudents)
{
int i,j,sizeOfChar;
for(i=0;i < numofStructs;i++)
{
do{
printf("Dwse plithos haraktirwn tou onomatos: ");
scanf("%d%*c",&sizeOfChar);
printf("sizeOfChar: %d\n",sizeOfChar);
if((sizeOfChar<MIN_STRUCT_ARRAY) || (sizeOfChar>MAX_ARRAY))
{
printf("Apodekti eisagwgi haraktirwn ews %d.\n",MAX_ARRAY);
}
}while((sizeOfChar<MIN_STRUCT_ARRAY) || (sizeOfChar>MAX_ARRAY));
printf("%d\n",sizeOfChar);
printf("Onoma mathiti: ");
memset(listOfstudents[i].name,0,MAX_ARRAY);
scanf("%[^\n]s%*c",listOfstudents[i].name);
printf("listOfstudents[i].name: %s\n",listOfstudents[i].name);
do{
printf("dwse to id tou mathiti: ");
scanf("%d",&(listOfstudents[i].id));
printf("listOfstudents[i].id: %d\n",listOfstudents[i].id);
if((listOfstudents[i].id < NULL_NUM) || (listOfstudents[i].id == NULL_NUM))
{
printf("Adunatio gia arnitiko i mideniko id.\n");
}
}while((listOfstudents[i].id < NULL_NUM) || (listOfstudents[i].id == NULL_NUM));
do{
printf("dwse bathmo: ");
scanf("%f%*c",&listOfstudents[i].marks);
printf("listOfstudents[i].marks: %f\n",listOfstudents[i].marks);
if((listOfstudents[i].marks < NULL_NUM) || (listOfstudents[i].marks == NULL_NUM) || listOfstudents[i].marks > MAX_MARKS)
{
printf("Adunatio gia arnitiko i mideniko id");
}
}while((listOfstudents[i].marks < NULL_NUM) || (listOfstudents[i].marks == NULL_NUM) || listOfstudents[i].marks > MAX_MARKS);
}
}
float mesosOros(int numofStructs, studentsDefPTR listOfstudents)
{
int i;
float total = 0;
for(i=0; i<numofStructs;i++)
{
total += listOfstudents[i].marks;
}
return total/numofStructs;
}
Review
The lines that begin with hash # are comments about the specific code change that follows. Save the patch to a file main.patch and apply to code sample main.c like this patch -o newmain.c -i main.patch main.c the output file will be newmain.c.
# Adding string.h b/c using memset to init name string
2a3
> #include <string.h>
# Creating typedef for pointer to struct students for readability
16a18
> typedef struct students* studentsDefPTR;
# Changing function arguments to accept pointer to struct students
# this allows the functions to access allocated memory b/c the function
# is told the address where the memory was allocated
18,19c20,21
< void scanStruct(int numofStructs,struct students listOfstudents);
< float mesosOros(int numofStructs,float grades[MAX_STRUCT_ARRAY]);
---
> void scanStruct(int numofStructs, studentsDefPTR listOfstudents);
> float mesosOros(int numofStructs, studentsDefPTR listOfstudents);
# insignifcant change, these are both equal and valid
23c25
< studentsDef* pStudents = NULL;
---
> studentsDefPTR pStudents = NULL;
# ensuring scanf reads newline, may not be the right thing, depends on input
# format, I choose to put each value on a newline, not significant to question
27c29
< scanf("&d",&structNum);
---
> scanf("%d%*c",&structNum);
# printing value read, good practice for ensuring initial coding does
# what is expected and is using the values you expect
32a35
> printf("structNum: %d\n",structNum);
# insignificant change, statements are equal, calling attentiong to PTR
34c37
< pStudents=(studentsDef*)malloc(structNum * sizeof(studentsDef));
---
> pStudents=(studentsDefPTR)malloc(structNum * sizeof(studentsDef));
# functions need the address of allocated memory
# pStudents is enough and holds the value returned by malloc
# this is a design change, my choice to make this problem
# easier for me, it may not be what is needed
41,42c44,45
< scanStruct(structNum,*pStudents);
< mesosOros(structNum,*pStudents.marks);
---
> scanStruct(structNum,pStudents);
> printf( "RES: %f\n", mesosOros(structNum,pStudents));
# changing scanStruct function to utilize pointer to allocated memory
# the allocated memory comes from malloc which provides the address to
# the first allocated struct in argument listOfstudents
47c50
< void scanStruct(int numofStructs,struct students listOfstudents)
---
> void scanStruct(int numofStructs,studentsDefPTR listOfstudents)
# ensuring scanf reads newline, may not be the right thing, depends on input
# format, I choose to put each value on a newline, not significant to question
# printing value read, good practice for ensuring initial coding does
# what is expected and is using the values you expect
54c57,58
< scanf("%d",&sizeOfChar);
---
> scanf("%d%*c",&sizeOfChar);
> printf("sizeOfChar: %d\n",sizeOfChar);
# oops, this is a duplicate
60c64
< // listOfstudents[i].name = (char*)malloc((sizeOfChar + 1) * sizeof(char));
---
> printf("%d\n",sizeOfChar);
# initialize the name string before setting, maybe consider initializing
# entire allocation after malloc instead, coders choice
#
# ensuring scanf reads newline, may not be the right thing, depends on input
# format, I choose to put each value on a newline, not significant to question
#
# printing value read, good practice for ensuring initial coding does
# what is expected and is using the values you expect
62c66,68
< scanf("%s", listOfstudents[i].name);
---
> memset(listOfstudents[i].name,0,MAX_ARRAY);
> scanf("%[^\n]s%*c",listOfstudents[i].name);
> printf("listOfstudents[i].name: %s\n",listOfstudents[i].name);
# ensuring scanf reads newline, may not be the right thing, depends on input
# format, I choose to put each value on a newline, not significant to question
#
# printing value read, good practice for ensuring initial coding does
# what is expected and is using the values you expect
66c72,73
< scanf("%d",&listOfstudents[i].id);
---
> scanf("%d",&(listOfstudents[i].id));
> printf("listOfstudents[i].id: %d\n",listOfstudents[i].id);
# ensuring scanf reads newline, may not be the right thing, depends on input
# format, I choose to put each value on a newline, not significant to question
#
# printing value read, good practice for ensuring initial coding does
# what is expected and is using the values you expect
75c82,83
< scanf("%f",&listOfstudents[i].marks);
---
> scanf("%f%*c",&listOfstudents[i].marks);
> printf("listOfstudents[i].marks: %f\n",listOfstudents[i].marks);
# changing mesosOros function to utilize pointer to allocated memory
# the allocated memory comes from malloc which provides the address to
# the first allocated struct in argument listOfstudents
84c92
< float mesosOros(int numofStructs,float grades[MAX_STRUCT_ARRAY])
---
> float mesosOros(int numofStructs, studentsDefPTR listOfstudents)
# new local scope value to hold total of all marks
86a95
> float total = 0;
# total up all marks into local variable
89c98
< grades[i]+=0;
---
> total += listOfstudents[i].marks;
# return computed result, maybe what is needed, not sure
# not significant to question though
91c100
< return grades[i]/numofStructs;
---
> return total/numofStructs;

Related

I have problem with reading input with blank spaces

I made this function to get input:
void entrada_dados(Time* time, int i){
scanf("%s %d %d", time[i].nome, &time[i].gols_marcados, &time[i].gols_sofridos);
};
The input is in this form:
2
Campinense
23
12
ABC
30
13
The main is:
int main(void) {
int n = 0;
scanf("%d", &n);
for(int i = 0; i < n; i++){
entrada_dados(time, i);
}
....
My problem is when the team name have some space like to "São Paulo". I have tried some forms to solve, but no one solved my problem.
I tried:
void entrada_dados(Time* time, int i){
fscanf(stdin, "%[^\n] %d %d", time[i].nome, &time[i].gols_marcados, &time[i].gols_sofridos);
};
and:
void entrada_dados(Time* time, int i){
fgets(time[i].nome, 100, stdin);
scanf("%d", &time[i].gols_marcados);
scanf("%d", &time[i].gols_sofridos);
}
but in the first case the output have nothing, and second case the output miss some cases. Someone can help me to understand this problem?
Edit 1:
The definition of .name is:
typedef struct Time{
char nome[100];
int gols_marcados;
int gols_sofridos;
} Time;
Edit 2:
Solution:
One way to solve it:
Try two fscanfs fscanf(stdin, " %[^\n]", time[i].nome);
fscanf(stdin, "%d %d", &time[i].gols_marcados, &time[i].gols_sofridos);
Thank you guys.
Because you have to handle strings with spaces, it's better to use fgets for those.
But mixing fgets and scanf doesn't work too well. We can replace scanf with fgets followed by sscanf.
To decode numbers, we can use strtol or sscanf
We take advantage of the fact that each element/member of Time appears on a separate line in the input file, so we can do fgets for every line. This simplifies the code and makes error checking easier.
Here is the refactored code. It is annotated.
I didn't do this, but, if these sequences are done a lot, we can combine some of these sequences in helper functions to reduce some code replication (e.g. a function that combines the fgets followed by the sscanf)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Time {
char nome[100];
int gols_marcados;
int gols_sofridos;
} Time;
// RETURNS: 1=valid, 0=syntax error
int
entrada_dados(Time *timelist, int i)
{
char buf[100];
char *cp;
Time *tim = &timelist[i];
int valid = 0;
do {
// get name
if (fgets(tim->nome,sizeof(tim->nome),stdin) == NULL)
break;
// strip newline
tim->nome[strcspn(tim->nome,"\n")] = 0;
// get number using strtol
if (fgets(buf,sizeof(buf),stdin) == NULL)
break;
tim->gols_marcados = strtol(buf,&cp,10);
if (*cp != '\n')
break;
// get number using sscanf
if (fgets(buf,sizeof(buf),stdin) == NULL)
break;
if (sscanf(buf,"%d",&tim->gols_sofridos) != 1)
break;
// all input is okay
valid = 1;
} while (0);
return valid;
};
int
main(void)
{
int n = 0;
#if 0
scanf("%d", &n);
#else
char buf[100];
if (fgets(buf,sizeof(buf),stdin) == NULL)
exit(1);
sscanf(buf,"%d",&n);
#endif
// allocate sufficient space
Time *timelist = malloc(sizeof(*timelist) * n);
// read in data
int valid = 0;
for (int i = 0; i < n; i++) {
valid = entrada_dados(timelist, i);
if (! valid)
break;
}
// show the data
if (valid) {
for (int i = 0; i < n; i++) {
Time *tim = &timelist[i];
printf("nome='%s' gols_marcados=%d gols_sofridos=%d\n",
tim->nome,tim->gols_marcados,tim->gols_sofridos);
}
}
return 0;
}
Here is the program input:
3
Campinense
23
12
ABC
30
13
São Paulo
17
82
Here is the program output:
nome='Campinense' gols_marcados=23 gols_sofridos=12
nome='ABC' gols_marcados=30 gols_sofridos=13
nome='São Paulo' gols_marcados=17 gols_sofridos=82

Struct and Pointers Stuck

I am facing some problems in my C assignment program:
At option #4, the grade only sorted to descending, while at #5, the grade won't change, only the name of the students and their scores are swapped.
At option #8, the string and float that inputted from file won't show up and I want the option 8 to be flexible (show from file when file was inputted via option #7 or only show the input from #1 menu option). Here is the example of the file:
80.64 John
90.40 Jane
78.00 Jake
The code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Studata{
float min, max;
int many;
char *max1, *min1, gx, gn;
}studata;
struct Student{
char name[100], grade;
float score[100];
};
float average(struct Student student[100]){
float sum;
for(int i=0; i<student.many; i++){
sum += studata[i].score;
}
return sum/(float)student.many;
}
void MM(struct Student student[100]){
int i;
studata.min = 0;
studata.max = 100;
for (i=0; i<studata.many; i++){
if(*student[i].score > studata.min){
studata.min = student[i].score;
studata.min1 = student[i].name;
studata.gn = student[i].grade;
}
}
for (i=0; i<studata.many; i++){
if(student[i].score < studata.min){
studata.max = student[i].score;
studata.max1 = student[i].name;
studata.gx = student[i].grade;
}
}
}
void swapname(char *a, char *b){
char z[100];
strcpy(z, a);
strcpy(a, b);
strcpy(b, z);
}
void swapscore(float a, float b){
float temporary = a;
a = b;
b = temporary;
}
void swapgrade(char A1, char B1) {
char C1 = A1;
A1 = B1;
B1 = C1;
}
void Bubblesort(int mode, struct Student student[100]) {
int i, j;
if(mode == 1) {
for (i=0; i<studata.many; i++) {
for (j=i+1; j<studata.many; j++) {
if(student[j].score > student[i].score) {
swapname(student[i].name, student[j].name);
swapscore(student[i].score, student[j].score);
swapgrade(student[i].grade, student[j].grade);
}
}
}
}
else if(mode == 0) {
for(i=0; i<studata.many; i++) {
for(j=i+1; j<studata.many; j++) {
if(student[j].score < student[i].score) {
swapname(student[i].name, student[j].name);
swapscore(student[i].score, student[j].score);
swapgrade(student[i].grade, student[j].grade);
}
}
}
}
}
int main(){
struct Student student[100];
int selection=1;
FILE *file;
while (selection <= 8 && selection >= 1) {
printf("\n\n\t-------MENU-------\n\n");
printf("0. Enter Data of Students\n");
printf("1. Calculate the Average\n");
printf("2. Show Maximum and Minimum\n");
printf("3. Sort Score Ascending\n");
printf("4. Sort Score Descending\n");
printf("5. Save Scores\n");
printf("6. Load Scores from File\n");
printf("7. Load All Data\n");
printf("Choice (Other than 1-8 to Exit): ");
scanf("%d", &selection);
if(selection == 1) {
printf("=============================\n");
printf("\nHow many students would you like to input: ");
scanf(" %d", &studata.many);
for (int i=0; i<studata.many; i++) {
printf("\nStudent-%d Name\t: ", i+1);
scanf(" %[^\n]s", student[i].name);
printf("Student-%d Score\t: ", i+1);
scanf(" %f", &student[i].score);
while(student[i].score > 100 || student[i].score < 0) {
printf("Hey, wrong input, please input correctly, okay?");
printf("\nStudent-%d Score\t: ", i+1);
scanf(" %f",&student[i].score);
}
if (student[i].score <= 100 && student[i].score >= 90 ) {
student[i].grade= 'A';
}
else if (student[i].score < 90 && student[i].score >= 80) {
student[i].grade= 'B';
}
else if (student[i].score < 80 && student[i].score >=70) {
student[i].grade= 'C';
}
else if (student[i].score < 70 && student[i].score >=60) {
student[i].grade= 'D';
}
else if (student[i].score < 60 && student[i].score >=50) {
student[i].grade= 'E';
}
else {
student[i].grade = 'F';
}
}
}
else if(selection == 2) {
printf("=============================\n");
printf("Average of Score is %.2f", average(student));
}
else if(selection == 3) {
MM(student);
printf("=============================\n");
printf("Minimum\t: %s || %4.2f || %c\n", studata.max1, studata.max, studata.gx);
printf("Maximum\t: %s || %4.2f || %c\n", studata.min1, studata.min, studata.gn);
}
else if(selection == 4) {
printf("=============================\n");
Bubblesort(0,student);
for(int i=0; i<studata.many; i++) {
printf(" %s : %5.2f --> %c\n", student[i].name, student[i].score, student[i].grade);
}
}
else if(selection == 5) {
printf("=============================\n");
Bubblesort(1,student);
for(int i=0; i<studata.many; i++) {
printf(" %s : %5.2f --> %c\n", student[i].name, student[i].score, student[i].grade);
}
}
else if(selection == 6) {
char filename[100];
printf("=============================\n");
printf("Name of the file (with ext.): ");
scanf(" %[^\n]s", filename);
file = fopen(filename, "w");
for(int i=0; i<studata.many; i++) {
fprintf(file,"%.2f %s\n", student[i].score, student[i].name);
}
fclose(file);
}
else if(selection == 7) {
char filename[100];
char sub_ch;
int i;
printf("Enter name of file you want to open (with extension): ");
scanf(" %[^\n]s", filename);
file = fopen(filename, "r");
while (file == NULL) {
printf("I'm Error! Reinput? (Y/n): ");
scanf("%c", &sub_ch);
if(sub_ch == 'Y') {
printf("Enter name of file you want to open (with extension): ");
scanf(" %[^\n]s", filename);
}
file = fopen(filename, "r");
if(sub_ch == 'n') {
exit(1);
}
}
printf("=============================\n");
fscanf(file, "%f %s", &student[i].score, student[i].name);
while (!feof(file)) {
if (student[i].score <= 100 && student[i].score >= 90 ) {
student[i].grade= 'A';
}
else if (student[i].score < 90 && student[i].score >= 80) {
student[i].grade= 'B';
}
else if (student[i].score < 80 && student[i].score >=70) {
student[i].grade= 'C';
}
else if (student[i].score < 70 && student[i].score >=60) {
student[i].grade= 'D';
}
else if (student[i].score < 60 && student[i].score >=50) {
student[i].grade= 'E';
}
else {
student[i].grade= 'F';
}
printf("%s %8.2f --> %c\n", student[i].name, student[i].score, student[i].grade);
fscanf(file, "%f %s", &student[i].score, student[i].name);
}
fclose(file);
}
else if(selection == 8) {
printf("=============================\n");
for (int i=0; i<studata.many; i++) {
printf("Name || Score || Grade\t: %s || %3.2f || %c\n", student[i].name, student[i].score, student[i].grade);
}
}
}
return 0;
}
I don't know what to do again after I tried to give pointer on every possible variable.
Expanding on the tips above, the thing that makes your code so difficult to read (and maintain) is you ignore (2) from the comment above. Your implementation (the logic applied to your data) is not separate from your interface (how you interact with the user). Instead you have your code all jumbled together in a menu that spans screenfuls of lines.
Keeping your implementation separate from interface keeps the logic for each separate and readable. Here are some additional areas where you can improve both the interface code and the implementation:
Don't Clutter The Implementation With Unnecessary Repeated Output Function Calls
During compilation, the compile with concatenate all adjacent string-literals that a separate only by whitespace. That means you do not need 10 separate calls to printf() to output your menu. In fact, since there are no conversions necessary in the menu you display, you don't need to call the variadic printf() function at all, much less call it 10 times. Since you want end-of-line control, a single call to fputs() is all you need, e.g.
fputs ("\n\n\t-----------MENU-----------\n\n"
" 0. Enter Data of Students\n"
" 1. Calculate the Average\n"
" 2. Show Maximum and Minimum\n"
" 3. Sort Score Ascending\n"
" 4. Sort Score Descending\n"
" 5. Save Scores\n"
" 6. Load Scores from File\n"
" 7. Load All Data\n\n"
"Choice (Other than 1-8 to Exit): ", stdout);
If you wanted a '\n' output at the end, then simply using puts() would do. A single call makes your menu readable.
Create Logical Functions To Implement Your Interface
You don't need to include the 10 lines of menu within the while loop in the body of your code. It makes things much more readable and maintainable if you create a short function to display the menu such as:
void show_menu (void)
{
fputs ("\n\n\t-----------MENU-----------\n\n"
" 0. Enter Data of Students\n"
" 1. Calculate the Average\n"
" 2. Show Maximum and Minimum\n"
" 3. Sort Score Ascending\n"
" 4. Sort Score Descending\n"
" 5. Save Scores\n"
" 6. Load Scores from File\n"
" 7. Load All Data\n\n"
"Choice (Other than 1-8 to Exit): ", stdout);
}
Then your main loop can be kept readable, e.g.
while (selection <= 8 && selection >= 1) {
show_menu();
That is far more readable and easier to maintain than 10 printf() functions at the beginning of your main loop.
Validate EVERY Input To Your Program
You must validate ever input to your program and every conversion. What happens if the user slips reaching for '4' and instead presses 'e' for the menu choice? Try it.
scanf("%d", &selection);
With no validation, you invoke Undefined Behavior when selection (which now holds an indeterminate value) is access in if(selection == 1). At minimum you must catch the error and exit, e.g.
if (scanf("%d", &selection) != 1) {
fputs ("error: invalid integer input.\n", stderr);
exit (EXIT_FAILURE);
}
To gracefully handle the error, you would need to know that with scanf(), when a matching-failure occurs, character extraction from the input stream ceases at that point leaving the offending characters causing the failure unread in the input stream. You must clear the offending characters before the next attempted input or the input will fail again for the same reason resulting in an infinite loop in many cases. There are hundreds of answers on this site showing how to correctly use scanf().
The better ones explain that you do not take user input with scanf(), but instead use fgets() with an adequately sized buffer (character array) and then use sscanf() to retrieve any values from the buffer you need -- which eliminates any possibility of characters being left unread in your input stream.
Don't Clutter Your Interface With Your Implementation
There is no reason the if .. else if .. else if ... logic for determining the letter grade should be in the middle of your program loop. Just as with show_menu() above, that implementation should be in a short function you can call from your main loop, e.g.
char get_ltrgrade (float f) /* return letter grade given float score */
{
if (f >= 90) return 'A';
else if (f >= 80) return 'B';
else if (f >= 70) return 'C';
else if (f >= 60) return 'D';
else return 'F';
}
Then instead of a dozen lines of implementation after if(selection == 1) , you simply have a short call to get_ltrgrade();
Match Your Data Structures To The Data You Have
Your use of struct Student and struct Studata does not fit the input data you show. The input data you show has one score per student. To match your data, struct Student alone would do. Now if you have many classes of students, then struct Studata starts to make more sense. float score[100]; would allow multiple grades per-student, but again that doesn't match the data you show, and you have no counter within struct Student to track the number of valid grades for that student. From what you show, float score: makes more sense.
Sort Arrays With qsort()
C provides qsort() to handle sorting arrays of any type. The qsort() function will be orders of magnitude more efficient than any sort you write and it has been tested for decades. New C programmers usually avoid qsort() because it requires writing a short compare() function that is generally less than 5-10 lines of code telling qsort() how to compare elements of the array. It is actually quite easy.
For explantion of the compare function used by qsort and how to use it, see How to sort an array of a structure using qsort? and further detail in How can I stop taking inputs from user when I press the key 'q'?.
The following puts together the pieces of your implementation that reads the student data from the file, assigning a letter grade to the score and storing all data in an array of struct. The program then calls qsort() to sort the array of struct and outputs the data. Since you include char grade; (char ltrgrade; below), the grade is assigned when the data is read from the file, eliminating the all need to compute the grade within the middle of your implementation.
#include <stdio.h>
#include <stdlib.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MAXN 128
#define MAXS 256
typedef struct { /* typedef allows use of student_t as type */
char name[MAXN],
ltrgrade;
float score;
} student_t;
char get_ltrgrade (float f) /* return letter grade given float score */
{
if (f >= 90) return 'A';
else if (f >= 80) return 'B';
else if (f >= 70) return 'C';
else if (f >= 60) return 'D';
else return 'F';
}
/* qsort compare by student_t->scoore (descending),
* change to >, < for ascending sort.
*/
int compare_score (const void *a, const void *b)
{
student_t *sa = (student_t*)a,
*sb = (student_t*)b;
return (sa->score < sb->score) - (sa->score > sb->score);
}
int main (int argc, char **argv) {
char buf[MAXC]; /* buffer to hold each line read from file */
student_t student[MAXS]; /* array of student_t (MAXS of them) */
size_t n = 0; /* array index (counter) */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
/* while array not full, read each line into buf, VALIDATE read */
while (n < MAXS && fgets (buf, MAXC, fp)) {
/* split line into score and name, VALIDATE conversion */
if (sscanf (buf, "%f %127[^\n]", &student[n].score, student[n].name) == 2) {
/* get letter grade based on score */
student[n].ltrgrade = get_ltrgrade (student[n].score);
n += 1; /* increment index only on valid converison */
}
else /* handle error */
fprintf (stderr, "error: invalid line format, student[%zu].\n", n);
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
qsort (student, n, sizeof *student, compare_score); /* sort by score */
for (size_t i = 0; i < n; i++) /* output result */
printf ("%-12s %6.2f %c\n",
student[i].name, student[i].score, student[i].ltrgrade);
return 0;
}
Note: in this case the compare() function takes a whole 2-lines of code (split into 3 for readability). Instead of the conditional (a < b) - (a > b) for sort descending, you could have used b - a, but that is prone to overflow when b is a large positive value and a a large negative value (or vice-versa). The difference of the conditional are used to return -1, 0, 1 for (-1 - a sorts before b), (0 - a and b are equal), and (1 - b sorts before a) which eliminates potential under/overflow.
Also note, qsorting any array, not matter how complicated, takes only 1-line of code:
qsort (student, n, sizeof *student, compare_score); /* sort by score */
Example Use/Output
Given your sample data in the file dat/studata.txt, the program operation and output is:
$ ./bin/studata dat/studata.txt
Jane 90.40 A
John 80.64 B
Jake 78.00 C
Whenever you go to write code that includes a user-interface, work to keep the interface and implementation separate (to the greatest extent possible). In theory, your interface should be a complete stand-alone program that doesn't depend on any given implementation. You should be about to operate and run your menu-system without having to worry about any other data (using dummy data where needed). Not only does this keep your code readable an maintainable, it also allows you to create your implementation logic in small testable units that do not depend on any given interface. This keeps your implementation short, readable and testable. There are always small areas of overlap, but your goal is to minimize and eliminate all but what is absolutely necessary.
There is a lot here, take your time going through it and if you have further questions, just let me know.

ADD elements of an array with a given condition

There is an array of n students( stu[n]).If gender is boy then my code adds
for boy b, 2nd,4th,6th,........even position elements of array and
for girl g, 1st,3rd,5th....odd position elements of array.
1> Gender of boys denoted by b.
2> Gender of girls denoted by g.
Input info>>
The 1st line contains n, denoting the number of students in the class, hence the number of elements in the array.
Each of the subsequent lines contains the marks of n students .
The last line contains gender b/g;
Output info>>
The output should contain the sum of all the alternate elements of marks as explained above.
#include <stdio.h>
int main() {
int n,i;
scanf("%d",&n);//n denotes number of students.
int stu[n],sum=0;
for(i=1;i<=n;++i)
scanf("%d",&stu[i]);//for accepting input in array.
char gen;
scanf("%s",&gen);//for accepting input gender b/g.
for(i=1;i<=n;i++){
if(gen=='g' && i%2!=0){ //girl g adds alternate odd position elements.
sum=sum+stu[i];
printf("%d",sum);
}
else if(gen=='b' && i%2==0){ //boy b adds alternate even position elements.
sum=sum+stu[i];
printf("%d",sum);
}
}
//code
return 0;
}
Sample Input
3
3
2
5
b
Sample Output
8
explanation>>
marks=[3,2,5] and gender = b so it will add 3+5(even position 0,2 alternate elements). If gender in place of b is g then it will produce output = 2.
My code is shows output of 0 in all test cases.
You have the major problem in
int n,i;
int stu[n],sum=0;
here, n being a uninitialized local scoped variable with automatic storage, the initial value is indeterminate.
Now, since the address of the variable was never taken and it has a type that can have trap representation, attempt to use the value (int stu[n]) will invoke undefined behavior.
You need to scan in the value into n first, then use that to define the VLA stu. Something like
int n,i;
scanf("%d",&n);//n denotes number of students.
// **Note: please check for errors in input with scanf return value.**
int stu[n],sum=0; // here, n has the scanned value.
That said,
char gen;
scanf("%s",&gen);
is also horribly wrong, you want to scan in a char, not a string, and with the address of a plain char variable, %s conversion specification would be UB, again. You should use %c and discard any whitespaces which is present in buffer altogether.
You're making things more complicated than they need to be. Here is how you can possibly do:
#include <stdio.h>
int main(void)
{
int mark;
int b = 0;
int g = 0;
char students_marks[5];
for (int i=0; i<5; i++) {
scanf("%d", &mark);
students_marks[i] = mark;
}
for (int i=0; i<5; i++) {
if (i%2 == 0) b += students_marks[i];
if (i%2 == 1) g += students_marks[i];
}
printf("Boys: %d\nGirls: %d\n", b, g);
return 0;
}
You should probably not use an array, and just ignore the first data point. It is (probably) easier to use a linked list. Or maybe just use two lists, alternating the inputs between them. And I would definitely not use scanf. If you are new to C, do NOT waste your time learning the foibles of the scanf format string language. The only time scanf is ever useful is in introductory courses where instructors incorrectly believe that you will be able to get input more quickly than if you spend time learning other methods. But in fact you will end up burning more time learning when to use spaces in the format string that you saved by not learning fread. After your introduction to C, you will (almost) never use scanf. Also, it seems like a horrible design to put the discriminant at the end of the input. The values to be summed (the gender) should be given as a command line argument. That said, you could just do:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
FILE *
xfopen(const char *path, const char *mode)
{
FILE *rv;
if( (rv = fopen(path, mode)) == NULL ) {
perror(path);
exit(EXIT_FAILURE);
}
return rv;
}
int
main(int argc, char **argv)
{
int i;
int size; /* Should probably be size_t. Skipping that for now. */
FILE *in = argc > 1 ? xfopen(argv[1], "r") : stdin;
int sum = 0;
if( fscanf(in, "%d", &size) != 1 || size <= 0 ) {
fprintf( stderr, "invalid input\n" );
exit(EXIT_FAILURE);
}
int grades[size];
for( i = 0; i < size; i++ ) {
if(fscanf(in, "%d", grades + i) != 1) {
fprintf( stderr, "invalid input on line %d\n", i );
exit(EXIT_FAILURE);
}
}
char gender;
if(fscanf(in, " %c", &gender) != 1) {
fprintf( stderr, "invalid input on line %d\n", i );
exit(EXIT_FAILURE);
}
if(strchr("bBgG", gender) == NULL) {
fprintf( stderr, "invalid gender: %c\n", gender);
exit(EXIT_FAILURE);
}
for( i = tolower(gender) == 'b'; i < size; i += 2 ) {
sum += grades[i];
}
printf("sum: %d\n", sum);
}
Hmm… i changed your code a Little bit and hope this runs as described.
int main() {
int n, index, sum=0;
int* stu;
scanf("%d", &n); // input number of studens
if(n>0)
stu = malloc(n*sizeof(int)); // allocate memory
else
return 0;
for(index=0;index<n;index++)
scanf("%d", &stu[index]);
char gen;
scanf("%c", &gen);
for(index=0; index<n; index++){
if(gen=='g' && index%2!=0) {
sum += stu[index];
printf("%d", sum);
} else if(gen=='b' && index%2==0) {
sum += stu[index];
printf("%d", sum);
}
}
return 0;
}

How do I sort my data?

I am new to programming so this is difficult for me. I need the program to end if the user's first input is -999, if not then they go on to input the account_num, last_name, and balance. But if the user enters -999 after the first input then it ends their input and displays the results. I cannot figure out how to get this whole -999 part to work while making my client[x].account_num be sorted in ascending order.. My code is below.
#include <stdio.h>
void bubble_sort(int[], int);
struct information
{
int account_num;
char last_name[30];
float balance;
};
int main()
{
/* declare variables */
struct information client[5];
int i, x, temp;
char c;
/* Prompt user */
printf ("Enter account number, last name, and balance.\n");
printf ("Enter -999 to end input:\n\n");
/* prompt user to enter number of people */
x = 0;
while (client[x].account_num != -999)
{
int tmp;
scanf ("%i", &tmp);
/* if types -999 as their first input program ends */
if (tmp == -999)
{
break;
}
else if (tmp < 1 || tmp > 1000)
{
printf ("*** Invalid account number. Please enter 1 - 1000 or -999 to exit ***\n");
}
else
{
client[x].account_num = tmp;
x ++;
}
bubble_sort(client[x].account_num, i);
scanf("%s", client[x].last_name);
while ( (c = getchar() != '\n') && c != EOF); /* clear input buffer. */
scanf("%.2f", &client[x].balance);
}
for (x = 0; x < 5; x++)
printf("%i\n", &client[x].account_num);
return 0;
}
void bubble_sort(int list[], int i)
{
int e, d, t;
for (e = 0; e < (i - 1); e++)
{
for (d = 0; d < i - e - 1; d++)
{
if (list[d] > list[d + 1])
{
/* swapping */
t = list[d];
list[d] = list[d + 1];
list[d + 1] = t;
}/* end for*/
}/*end for*/
}/* end for */
}/* end function */
There are several issues here. First, you don't want to index account_num and balance in the struct, unless you want to have several account numbers and balances per account holder. I also suggest replacing the for with a while loop so you don't have to fiddle with the loop counter x in case the user enters an invalid account number. Finally, for the sake of clarity I introduced a temporary variable tmpfor input and performed all tests on it, assigning the content to the account structure only if all validity tests were passed. tmpis only needed inside the whileloop which is why I declared it there, instead of making it part of the declarations in main().
#include <stdio.h>
#define MAX_CLIENTS 5
#define MAX_ACCOUNTS 1000
struct information
{
int account_num;
char last_name[30];
float balance;
};
int main()
{
/* declare variables */
struct information client[MAX_CLIENTS];
int i, x, people;
char c;
/* Prompt user */
printf ("Enter account number, last name, and balance.\n");
printf ("Enter -999 to end input:\n\n");
/* prompt user to enter number of people */
x = 0;
while (x < MAX_CLIENTS)
{
int tmp;
scanf ("%i", &tmp);
/* if types -999 as their first input program ends */
if (tmp == -999)
{
break;
}
else if (tmp < 1 || tmp > MAX_ACCOUNTS)
{
printf ("*** Invalid account number. Please enter 1 - %d or -999 to exit ***\n", MAX_ACCOUNTS);
}
else
{
client[x].account_num = tmp;
x ++;
}
}
return 0;
}

In C, how can a store long strings (example passwords)

Well, I've got this programm checking passwords. It works fine if I set the second array (that is the for loop) to 8 digits. But as soon as the pw needs to be longer than 8 digits the whole thing goes wrong (since the for loop goes for 10 digits).
I thought declaring the first array to be MAXLINE long would work, but it doesn't seem to solve the issue.
/* IMPORT ---------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* CONST------------------ */
#define MAXDIGIT 10000
/* VARIABLES (global) ---------- */
/* MAIN--------------- */
int main()
{
/* VARIABLES (local) --------- */
/* VARIABLES (local) --------- */
// ENTERED PW:
char EnterCode[MAXDIGIT];
int i;
// REAL PW:
char arr[MAXDIGIT] = "123456789"; //"YKJ98LGDDF";
int j;
printf("PW: "); // for testing
for (j = 0 ; j < 8; ++j){
printf("%c", arr[j]);
}
/* Intro --------------------- */
printf("\nPlease enter code of authorization: ");
for(i = 0; i < 10; ++i){
scanf("%c", &EnterCode[i]);
printf("%c", EnterCode[i]); // test 1
}
if (strcmp(EnterCode,arr) == 0){
printf("\nAccess authorized.\n");
}else{
printf("\nAccess denied!\n");
}
system("PAUSE");
return 0;
}
Although you can put scanf in a loop, you do not need to do that in your application:
If the passcode is going to be captured in a string, simply declare a reasonable length string, use it to read user's input in a single call:
char EnterCode[20];//20 is just for illustration, pick any reasonable length for your application
printf("enter your passcode:\n");
scanf("%19s", EnterCode); //limit characters read to 19 (leaving room for terminating NULL)
For an exceptionally long pass-code,
rather than creating memory on the stack:
#define MAXDIGIT 10000
char EnterCode[MAXDIGIT];//uses a large block of memory from a limited source
Place it on the heap:
char *EnterCode = {0};
EnterCode = malloc(MAXDIGIT); //also uses a large block of memory, but from a much much larger source
When you are done using EnterCode, free the memory:
free(EnterCode);
In C language strings end with '\0'.
Therefore, your password should be "123456789" and after entering the "EnterCode"
set EnterCode[10] = '\0' (in your code).
Replace
for(i = 0; i < 10; ++i){
scanf("%c", &EnterCode[i]);
printf("%c", EnterCode[i]); // test 1
}
with
scanf("%s", EnterCode);
and try again.

Resources