Yes, hello. I'm really new to programming. I genuinely need help because I would like to both understand what I'm doing wrong and also pass my class.
I'm in a Intro to Programming class and my assignment is to create a program that uses an array to store percentage grades in the range from 0 to 100 (inclusive). The program should allow the user to indicate when they are done entering grades. When the user is done entering grades, the program should print out the grades entered.
I have a running code, compiled in Code::Blocks. But my questions/problems are:
A.) What's the best way to tell C when the user is done? Should I
Leave the code as is and let any key be hit?
Add a specific variable like 'done'?
Do something else?
B.) How do I print only the grades that have been entered without it going through all 100 slots? I can't for the life of me find the solution.
Any and all suggestions welcome and appreciated!!!
int i = 0;
float percentScore[100];
for (i = 0; i < 10; i++) {
printf("Grade %d: ", i + 1);
scanf("%f", &percentScore[i]);
}
for (i = 0; i < 10; i++) {
printf("\n%.2f%%", percentScore[i]);
}
return 0;
Your choices for A) are not mutually exclusive; the first is something the user can do, the second is a way to represent that in the code. As such, you could reasonably do both.
As for B), you need a way to represent the number of grades entered (hint: a variable); this can then be used to control how many get printed.
Here is a simple approach for your problem:
for each input, read a full line from the user ;
try and convert this line as a number with sscanf() ;
if the conversion fails, consider the input exhausted ;
if the value is out of bounds, restart this input.
The user can thus signal the end of the list by entering a blank line.
Here is a example:
#include <stdio.h>
#define GRADE_NB 100
int main(void) {
int i, n;
float grade, percentScore[GRADE_NB];
char buf[100];
for (n = 0; n < GRADE_NB;) {
printf("Grade %d: ", n + 1);
if (!fgets(buf, sizeof buf, stdin)) {
/* end of file reached: input exhausted */
break;
}
if (sscanf(buf, "%f", &grade) != 1) {
/* not a number: input exhausted */
break;
}
if (grade >= 0 && grade <= 100) {
/* value is OK, grade accepted */
percentScore[n] = grade;
n++;
} else {
printf("Invalid grade: %f\n", grade);
}
}
for (i = 0; i < n; i++) {
printf("%.2f%%\n", percentScore[i]);
}
return 0;
}
If the potential number of inputs is not limited, you must allocate the array from the heap and reallocate it as more inputs are gathered. Here is a simple solution for this where the array is reallocated for each input:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i, n;
float grade, *percentScore;
char buf[100];
for (n = 0, percentScore = NULL;;) {
printf("Grade %d: ", n + 1);
if (!fgets(buf, sizeof buf, stdin)) {
/* end of file reached: input exhausted */
break;
}
if (sscanf(buf, "%f", &grade) != 1) {
/* not a number: input exhausted */
break;
}
if (grade >= 0 && grade <= 100) {
/* value is OK, grade accepted */
percentScore = realloc(percentScore, (n + 1) * sizeof(*percentScore));
if (percentScore == NULL) {
printf("allocation failed\n");
return 1;
}
percentScore[n] = grade;
n++;
} else {
printf("Invalid grade: %f\n", grade);
}
}
for (i = 0; i < n; i++) {
printf("%.2f%%\n", percentScore[i]);
}
free(percentScore);
return 0;
}
Related
The program isn't printing after giving me the first chance to guess.
#include <stdio.h>
#include <string.h>
int main() {
char menu;
int c = 0, flag = 0, life = 8;
printf("\nWelcome to Hangman!!!");
printf("\nThis is a game of hangman.");
printf("Player 1 enters a random word and the other has to guess it.");
printf("You get 8 lives in total i.e. you can have a maximum of 8 wrong guesses.");
printf("\n");
printf("Press n for new game\n");
printf("Press q to quit\n");
printf("\n");
scanf("%c", &menu);
int i = 0, j = 0;
char w[20], ch;
if (menu == 'q') {
printf("Exiting...");
printf("Thanks for playing");
}
else if (menu == 'n') {
printf("Player 1 enters a word\n");
scanf("%s", w);
int len = strlen(w);
for (int i = 0; i < len; i++) {
toupper(w[i]);
}
printf("\e[1;1H\e[2J");
char arr[len - 1];
for (int i = 0; i < len - 1; i++) {
arr[i] = '_';
printf("%c", arr[i]);
}
printf("\n");
while (life != 0) {
for (int i = 0; i < len - 1; i++) {
if (arr[i] == '_') {
flag = 1;
break;
}
else {
flag = 0;
}
}
if (flag == 0) {
printf("You Won!!\n");
printf("You Guessed The Word: %s", w);
break;
}
else {
char ans;
printf("Enter a letter between A-Z");
scanf("%c", ans);
toupper(ans);
for (int j = 0; j < len; j++) {
if (ans == w[j]) {
arr[j] = ans;
c++;
}
}
if (c == 0) {
life--;
}
c = 0;
for (int j = 0; j < len; j++) {
printf("%c", arr[j]);
}
printf("\n Lives Remaining= %d \n", life);
}
}
if (life == 0) {
printf("\n You Lost!!! \n");
printf("The Word Was: %s", w);
}
}
else {
printf("Invalid Character");
}
}
Output:
Welcome to Hangman!!!
This is a game of hangman.Player 1 enters a random word and the other has to >guess it.You get 8 lives in total i.e. you can have a maximum of 8 wrong >guesses.
Press n for new game
Press q to quit
n
Player 1 enters a word
Hello
Enter a letter between A-Z
PS C:\Users\arora\Desktop\Programs\C>
There are quite a few problems with your program. Here are the major ones:
You want to use use space prefix in the format string for scanf(" %c", ...) to ensure previous newlines are ignored.
scanf("%c", ans); should be &ans. It causes scanf() to fail rendering the remain of the program non-interactive. Without input from the user the core game logic doesn't work.
Here are some of the other issues:
#include <ctype.h>.
(not fixed) Consider changing the menu logic so 'q' quits, and any other letter starts a game.
Game prompt contains long lines that are hard to read for the player(s).
You use a printf() per line which makes it hard to read. Use a single call and multi-line strings as input.
Try to branch your code less by making use of early return. It makes it easier to read.
Check the return value of scanf(). If it fails then whatever variable it read doesn't have a well defined value.
Ensure that scanf() read no more than 19 bytes into a 20 byte array w. It takes a little macro magic to generate the 19 so I didn't make this change but it's a good idea to #define constants for magic values like the 20.
arr is not \0 terminated (len-1). Most c programmers expect a string so it's not worth the confusion to save 1 byte.
Use a function or macro for the ANSI escape to clear the screen.
Eliminate unused variables i, j.
Reduce scope of variables (declare variables close to where you use them).
The calculation of the flag variable is cumbersome.
(not fixed) The prompt "Enter a letter between A-Z" is somewhat ambiguous. Suggest "... between A and Z".
It's generally a good idea to leave user input as you read. If you care about the repeated toupper() you can create a copy of the user input with letters in upper case, and create another variable to hold the upper case version of the player's guess. This avoid you saying things like you entered the word "BOB" when the actual input was "bob".
You attempt to use toupper() to convert each letter to upper case but don't assign the result to anything so it does not do anything constructive.
Consider some functions to document what each your code does. I added some comments for now.
(mostly not fixed) Consider using better variable names (c, w, arr, flag).
(not fixed) Should you reject a word with your magic '_' value? In general should you validate that the word is reasonable (a-z, len > 0, len < 20)?
(not fixed) Consider, in arr, just storing if a letter was correctly guess (boolean). When evaluating the state show the letter from w if it is already guessed otherwise the _.
(not fixed) If you guess a correct letter again, it's considered a good guess. Should it?
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#define clear() printf("\e[1;1H\e[2J")
int main() {
printf(
"Welcome to Hangman!!!\n"
"\n"
"This is a game of hangman.\n"
"Player 1 enters a random word and the other has to guess it.\n"
"You get 8 lives in total i.e. you can have a maximum of 8 wrong guesses.\n"
"\n"
"Press n for new game\n"
"Press q to quit\n"
);
char menu;
if(scanf(" %c",&menu) != 1) {
printf("scanf failed\n");
return 1;
}
switch(menu) {
case 'q':
printf(
"Exiting..."
"Thanks for playing\n"
);
return 0;
case 'n':
break;
default:
printf("Invalid Character");
return 1;
}
printf("Player 1 enters a word\n");
char w[20];
if(scanf("%19s", w) != 1) {
printf("scanf failed\n");
return 1;
}
clear();
char arr[20];
int len=strlen(w);
for(int i=0;i<len;i++) {
arr[i]='_';
}
arr[len] = '\0';
int life=8;
for(;;) {
printf("%d Lives Remaining\n", life);
// read a guess from player
for(int i = 0; i < len; i++) {
printf("%c", arr[i]);
}
printf(" Enter a letter between A-Z ");
char guess;
if(scanf(" %c", &guess) != 1) {
printf("scanf failed\n");
return 1;
}
// determine if any of the letters are in the secret word
int c = 0;
for(int i=0; i<len; i++) {
if(toupper(guess) == toupper(w[i])) {
arr[i]=guess;
c = 1;
}
}
if(c==0) {
life--;
}
// game over?
int flag = 0;
for(int i = 0; i<len; i++) {
if(arr[i]=='_') {
flag=1;
break;
}
}
if(flag==0) {
printf("You Won!!\n");
printf("You Guessed The Word: %s\n",w);
break;
}
if(life==0) {
printf("\n You Lost!!!\n");
printf("The Word Was: %s\n", w);
break;
}
}
}
Example: input: 420 50 -4
output: Numbers 3
Positive 2
Negative 1
and also for the same code:
input: 420 50 -4 7
output: Numbers 4
Positive 3
Negative 1
#include<stdio.h>
#define N 2
int main()
{
int a[N], i=0, n=0, k=0, z=0;
for(i=0; i<N; i++)
{
scanf("%d" , &a[i]);
if((a[i] >= -10000 && a[i] <= 10000 ))
n++;
if(a[i]>0)
k++;
if(a[i]<0)
z++;
}
printf("Numbers:%d \n", n);
printf("Positive:%d \n", k);
printf("Negative:%d \n", z);
return 0;
}
new issue
So the idea is this, I need my programm(mostly done by yano here) to only be able to load numbers ranging for -10000 to 10000 including border numbers, if other numbers would be loaded, the program should print the correct numbers, and ignore the incorrect (more like remove from array and replacing the element with the rest, which is correct, whilst reducing the total number of elements in the array)
example
input 140 -154161 20 30
output 140, 20, 30
Error: Input is outside interval!"
#include <stdio.h>
#include <stdlib.h>
#define INITIAL_SIZE 10
void
printArray (const int *myArray, size_t numsEntered)
{
int i, c = 0, k = 0, z = 0, s=0, l=0, sum=0, max, min;
float pk, pz, ps, pl, prumer;
for (size_t i = 0; i < numsEntered; i++) //does math
{
sum = sum + myArray[i];
if (i)
printf (", ");
printf ("%i", myArray[i]);
if ((myArray[i] >= -10000 && myArray[i] <= 10000))
c++;
if (myArray[i] > 0)
k++;
if (myArray[i] < 0)
z++;
if(myArray[i]%2==0)
s++;
else
l++;
max = myArray[0];
min = myArray[0];
if(myArray[i] > max)
{
max = myArray[i];
}
if(myArray[i] < min)
{
min = myArray[i];
}
}
if ((myArray[i] >= -10000 && myArray[i] <= 10000)) //checks if arrays are in range
{
prumer=(float) sum/2;
pk = (float) k / c;
pz = (float) z / c;
ps = (float) s / c;
pl = (float) l / c;
printf ("\n");
printf ("Pocet cisel: %d\n", c);
printf ("Pocet kladnych: %d\n", k);
printf ("Pocet zapornych: %d\n", z);
printf ("Procento kladnych: %.2lf\n", pk);
printf ("Procento zapronych: %.2lf\n", pz);
printf("Pocet sudych: %d\n", s);
printf("Pocet lichych: %d\n", l);
printf ("Procento sudych: %.2lf\n", ps);
printf ("Procento lichych: %.2lf\n", pl);
printf("Prumer: %.2lf\n", prumer );
printf("Maximum: %d\n", max);
printf("Minimum: %d\n", min);
}
if (myArray[0]<-10000 || myArray[0]>10000) //checks if first element is in wrong range
programm prints arror and returns 0
{
printf("\n");
printf ("Error: Vstup je mimo interval!");
}
}
int
main ()
{
int lastArray = 0, end = 0, b = 0, i=0;
size_t arraySize = INITIAL_SIZE;
size_t numsEnteredSoFar = 0;
int *myArray = malloc (sizeof (*myArray) * arraySize);// initially make room for 10
if (myArray == NULL)
exit (-1);
while (1)
{
int curEntry, size = sizeof (myArray) / sizeof (int);
char ch;
if (scanf ("%d", &curEntry) == 1)
{
b = curEntry;
end = numsEnteredSoFar;
ch = fgetc (stdin);
myArray[numsEnteredSoFar++] = curEntry;
if (numsEnteredSoFar == arraySize)
{
arraySize += INITIAL_SIZE;
int *temp = realloc (myArray, arraySize * sizeof (*myArray));
if (temp == NULL)
{
fprintf (stderr, "out of memory\n");
exit (-1);
}
else
{
myArray = temp;
}
}
}
for (size_t i = 0; i < numsEnteredSoFar; i++)
if((myArray[i]<-10000 || myArray[i]>10000)) //checks if input is in range if not exits
{
if (i) //my attempt for making this work
printf (", ");
printf ("%i", myArray[i]);
printf ("\n");
printf ("Error: Vstup je mimo interval!");
exit (-1);
}
if (ch == 10)
{
break;
}
}
printArray (myArray, numsEnteredSoFar);
free (myArray);
return 0;
}
There are several ways to solve this problem:
Declare an array that's large enough to accommodate the largest conceivable size of your data.
Include a size at the beginning of your data, and use that to malloc your array.
Use a data structure that doesn't depend on a fixed size, such as a linked list.
This example allows the user to enter numbers "indefinitely" without the need for prompting how many to enter. Of course, your computer only has so much RAM, so there is a limit, but not a practical limit. Essentially, you need to choose an initial size, then allocate more space dynamically when that size is reached.
#include <stdio.h>
#include <stdlib.h>
#define INITIAL_SIZE 10
void printArray(const int* myArray, size_t numsEntered)
{
for (size_t i=0; i<numsEntered; i++)
{
printf("myArray[%zu] = %d\n", i, myArray[i]);
}
}
int main(void)
{
size_t arraySize = INITIAL_SIZE;
size_t numsEnteredSoFar = 0;
int* myArray = malloc(sizeof(*myArray) * arraySize); // initially make room for 10
if (myArray == NULL) exit(-1); // whoops, malloc failed, handle this error how you want
while(1)
{
int curEntry;
printf("enter a number, or 'q' to quit: ");
if (scanf("%d", &curEntry) == 1)
{
// store in the array, increment number of entries
myArray[numsEnteredSoFar++] = curEntry;
// here you can check for positives and negatives, or
// wait to do that at the end. The point of this example
// is to show how to dynamically increase memory allocation
// during runtime.
if (numsEnteredSoFar == arraySize)
{
puts("Array limit reached, reallocing");
// we've reached our limit, need to allocate more memory to continue.
// The expansion strategy is up to you, I'll just continue to add
// INITIAL_SIZE
arraySize += INITIAL_SIZE;
int* temp = realloc(myArray, arraySize * sizeof(*myArray));
if (temp == NULL)
{
// uh oh, out of memory, handle this error as you want. I'll just
// print an error and bomb out
fprintf(stderr, "out of memory\n");
exit(-1);
}
else
{
// realloc succeeded, we can now safely assign temp to our main array
myArray = temp;
}
}
}
else
{
// the user entered 'q' (or anything else that didn't match an int), we're done
break;
}
}
// print the array just to show it worked. Instead, here you can
// loop through and do your comparisons for positive and negative,
// or you can continue to track that after each entry as you've
// shown in your code
printArray(myArray, numsEnteredSoFar);
free(myArray);
return 0;
}
Demo
Several parts to the answer.
Either declare a nice, big array, bigger than you'll ever need, or, prompt the user for the size, and then use that user-entered size to declare or allocate the array. (This is a popular strategy, but it's a lousy user experience, since the user shouldn't need to know or say how many numbers they're going to enter.)
Check the return value of scanf. If the return value isn't 1, this means that scanf failed, and didn't input a number. You can take this as an indication that the user stopped entering numbers.
Have two variables: the size of the array, and the number of numbers actually entered. You set the number of numbers actually entered by noticing when scanf failed. Then, later, when you work with the date in the array, you don't do for(i = 0; i < N; i++), you do for(i = 0; i < number_of_numbers; i++).
If you don't want to ask the user to explicitly enter the number of numbers, and you don't want to pick a "big enough" size in advance (either because you don't want to waste memory, or because you want to make sure the user can enter a lot of inout, potentially more than any number you picked), it's possible to dynamically reallocate an array bigger and bigger as the user enters more and more data, but that's an advanced topic.
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.
writing a program that will be finding min, max, avg of values entered by user. Having trouble writing something that will check to make sure there are only postive integers entered and produce an error message. heres my for statement that is reading the input so far:
for (int value = 0; value <= numofvals; ++value) {
printf("Value %d: %f\n", value, val_input);
scanf("%f", &val_input);
}
mind you I've been learning code for about 3 weeks and was just introduced to loops this week so my understanding is rudimentary at best!
First, don't use scanf. If stdin doesn't match what it expects it will leave it in the buffer and just keep rereading the same wrong input. It's very frustrating to debug.
const int max_values = 10;
for (int i = 0; i <= max_values; i++) {
int value;
if( scanf("%d", &value) == 1 ) {
printf("Got %d\n", value);
}
else {
fprintf(stderr, "I don't recognize that as a number.\n");
}
}
Watch what happens when you feed it something that isn't a number. It just keeps trying to read the bad line over and over again.
$ ./test
1
Got 1
2
Got 2
3
Got 3
foo
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
I don't recognize that as a number.
Instead, use fgets to reliably read the whole line and sscanf to parse it. %f is for floats, decimal numbers. Use %d to recognize only integers. Then check if it's positive.
#include <stdio.h>
int main() {
const size_t max_values = 10;
int values[max_values];
char buf[1024];
size_t i = 0;
while(
// Keep reading until we have enough values.
(i < max_values) &&
// Read the line, but stop if there's no more input.
(fgets(buf, sizeof(buf), stdin) != NULL)
) {
int value;
// Parse the line as an integer.
// If it doesn't parse, tell the user and skip to the next line.
if( sscanf(buf, "%d", &value) != 1 ) {
fprintf(stderr, "I don't recognize that as a number.\n");
continue;
}
// Check if it's a positive integer.
// If it isn't, tell the user and skip to the next line.
if( value < 0 ) {
fprintf(stderr, "Only positive integers, please.\n");
continue;
}
// We got this far, it must be a positive integer!
// Assign it and increment our position in the array.
values[i] = value;
i++;
}
// Print the array.
for( i = 0; i < max_values; i++ ) {
printf("%d\n", values[i]);
}
}
Note that because the user might input bad values we can't use a simple for loop. Instead we loop until either we've read enough valid values, or there's no more input.
Something easy like this may work for you:
int n;
int ret;
for (;;) {
ret = scanf("%d", &n);
if (ret == EOF)
break;
if (ret != 1) {
puts("Not an integer");
for (;;)
if (getchar() == '\n')
break;
continue;
}
if (n < 0) {
puts("Not a positive integer");
continue;
}
printf("Correct value %d\n", n);
/* Do your min/max/avg calculation */
}
/* Print your results here */
This is just an example and assumes you do not need to read floating point numbers and then check if they are integers, as well as a few other things. But for starters, it is simple and you can work on top of it.
To break out of the loop, you need to pass EOF (typically Ctrl+D in Linux/macOS terminals, Ctrl+Z in Windows ones).
An easy and portable solution
#include <limits.h>
#include <stdio.h>
int get_positive_number() {
char buff[1024];
int value, ch;
while (1) {
printf("Enter positive number: ");
if (fgets(buff, 1023, stdin) == NULL) {
printf("Incorrect Input\n");
// Portable way to empty input buffer
while ((ch = getchar()) != '\n' && ch != EOF)
;
continue;
}
if (sscanf(buff, "%d", &value) != 1 || value < 0) {
printf("Please enter a valid input\n");
} else {
break;
}
}
return value;
}
void solution() {
// Handling malformed input
// Memory Efficient (without using array to store values)
int n;
int min = INT_MAX;
int max = INT_MIN;
double avg = 0;
printf("Enter number of elements: ");
scanf("%d", &n);
getc(stdin);
int value;
for (int i = 0; i < n; i++) {
value = get_positive_number();
if (value > 0) {
if (min > value) {
min = value;
}
if (max < value) {
max = value;
}
avg += value;
}
}
avg = avg / n;
printf("Min = %d\nMax = %d\nAverage = %lf\n", min, max, avg);
}
int main() {
solution();
return 0;
}
Output:
Enter number of elements: 3
Enter positive number: 1
Enter positive number: 2
Enter positive number: a
Please enter a valid input
Enter positive number: -1
Please enter a valid input
Enter positive number: 1
Min = 1
Max = 2
Average = 1.333333
The question is that show the digits which were repeated in C.
So I wrote this:
#include<stdio.h>
#include<stdbool.h>
int main(void){
bool number[10] = { false };
int digit;
long n;
printf("Enter a number: ");
scanf("%ld", &n);
printf("Repeated digit(s): ");
while (n > 0)
{
digit = n % 10;
if (number[digit] == true)
{
printf("%d ", digit);
}
number[digit] = true;
n /= 10;
}
return 0;
}
But it will show the repeated digits again and again
(ex. input: 55544 output: 455)
I revised it:
#include<stdio.h>
int main(void){
int number[10] = { 0 };
int digit;
long n;
printf("Enter a number: ");
scanf("%ld", &n);
printf("Repeated digit(s): ");
while (n > 0)
{
digit = n % 10;
if (number[digit] == 1)
{
printf("%d ", digit);
number[digit] = 2;
}
else if (number[digit] == 2)
break;
else number[digit] = 1;
n /= 10;
}
return 0;
}
It works!
However, I want to know how to do if I need to use boolean (true false), or some more efficient way?
To make your first version work, you'll need to keep track of two things:
Have you already seen this digit? (To detect duplicates)
Have you already printed it out? (To only output duplicates once)
So something like:
bool seen[10] = { false };
bool output[10] = { false };
// [...]
digit = ...;
if (seen[digit]) {
if (output[digit])) {
// duplicate, but we already printed it
} else {
// need to print it and set output to true
}
} else {
// set seen to true
}
(Once you've got that working, you can simplify the ifs. Only one is needed if you combine the two tests.)
Your second version is nearly there, but too complex. All you need to do is:
Add one to the counter for that digit every time you see it
Print the number only if the counter is exactly two.
digit = ...;
counter[digit]++;
if (counter[digit] == 2) {
// this is the second time we see this digit
// so print it out
}
n = ...;
Side benefit is that you get the count for each digit at the end.
Your second version code is not correct. You should yourself figured it out where are you wrong. You can try the below code to print the repeated elements.
#include<stdio.h>
int main(void){
int number[10] = { 0 };
int digit;
long n;
printf("Enter a number: ");
scanf("%ld", &n);
printf("Repeated digit(s): ");
while (n > 0)
{
digit = n % 10;
if (number[digit] > 0)
{
number[digit]++;;
}
else if (number[digit] ==0 )
number[digit] = 1;
n /= 10;
}
int i=0;
for(;i<10; i++){
if(number[i]>0)
printf("%d ", i);
}
return 0;
}
In case you want to print the repeated element using bool array (first version) then it will print the elements number of times elements occur-1 times and in reverse order because you are detaching the digits from the end of number , as you are seeing in your first version code output. In case you want to print only once then you have to use int array as in above code.
It is probably much easier to handle all the input as strings:
#include <stdio.h>
#include <string.h>
int main (void) {
char str[256] = { 0 }; /* string to read */
char rep[256] = { 0 }; /* string to hold repeated digits */
int ri = 0; /* repeated digit index */
char *p = str; /* pointer to use with str */
printf ("\nEnter a number: ");
scanf ("%[^\n]s", str);
while (*p) /* for every character in string */
{
if (*(p + 1) && strchr (p + 1, *p)) /* test if remaining chars match */
if (!strchr(rep, *p)) /* test if already marked as dup */
rep[ri++] = *p; /* if not add it to string */
p++; /* increment pointer to next char */
}
printf ("\n Repeated digit(s): %s\n\n", rep);
return 0;
}
Note: you can also add a further test to limit to digits only with if (*p >= '0' && *p <= '9')
output:
$./bin/dupdigits
Enter a number: 1112223334566
Repeated digit(s): 1236
Error is here
if (number[digit] == true)
should be
if (number[digit] == false)
Eclipse + CDT plugin + stepping debug - help you next time
As everyone has given the solution: You can achieve this using the counting sort see here. Time complexity of solution will be O(n) and space complexity will be O(n+k) where k is the range in number.
However you can achieve the same by taking the XOR operation of each element with other and in case you got a XOR b as zero then its means the repeated number. But, the time complexity will be: O(n^2).
#include <stdio.h>
#define SIZE 10
main()
{
int num[SIZE] = {2,1,5,4,7,1,4,2,8,0};
int i=0, j=0;
for (i=0; i< SIZE; i++ ){
for (j=i+1; j< SIZE; j++){
if((num[i]^num[j]) == 0){
printf("Repeated element: %d\n", num[i]);
break;
}
}
}
}