Struct and Pointers Stuck - c

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.

Related

I'm trying to make a program in C in which you can play hangman but as soon as it prints the option to guess the letter the program terminates

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

How to filter out characters in C programming with exceptions

I am trying to write a C program that asks the user for an input and if the input is between a given float an output will appear. in this case if the input speed is 20.5 then the output would be you are in second gear. I want to filter out everything invalid input.
I already have some kind of a filter but it is not working well. if the in put is "afgegq" it will be filter and ask me again for input. But if it's 156FFAGFAE. it will be not filtered.
#include <stdio.h>
#include <stdlib.h>
int main()
{
for (;;)
{
int versnelling[5] = {1, 2, 3, 4, 5};
float speed = 0;
printf("Please enter a speed: ");
if (scanf("%f", &speed) != 1)
{
fputs("error: invalid input. Please enter a number\n",stderr);
return 0;
}
if (0.0 < speed && speed < 10.0)
{
printf("The gear you are in is: %i\n", versnelling[0]);
}
else if (speed >= 10.0 && speed < 30.0)
{
printf("The gear you are in is: %i\n", versnelling[1]);
}
else if (speed >= 30.0 && speed < 60.0)
{
printf("The gear you are in is: %i\n", versnelling[2]);
}
else if (speed >= 60.0 && speed < 80.0)
{
printf("The gear you are in is: %i\n", versnelling[3]);
}
else if (speed >= 80.0 && speed <= 100)
{
printf("The gear you are in is: %i\n", versnelling[4]);
}
else if (speed > 100)
{
printf("I can not go faster then 100km/h \n");
}
else if (speed == 0)
{
printf("The gear you are in is: Neutral\n");
}
else if (speed < 0 && speed > -15)
{
printf("The gear you are in is: R\n");
}
else if (speed < -15)
{
printf("I can not go that fast in reverse\n");
}
}
}
I want it to filter out everything but the numbers and the letter Q. I need the letter Q because I want to create an average of the input(I will try to find out how later). but for know I need this. Thank you in advance.
The best way to handle errant user input is to first read a line and then parse it for a float with strtof().
printf("Please enter a speed: ");
fflush(stdout); // Insure prompt is printed first
char buf[100];
if (fgets(buf, sizeof buf, stdin) == NULL) {
printf("End of file\n");
return 0;
}
if (toupper((unsigned char) *buf) == 'Q') == NULL) {
printf("Q detected\n");
break;
}
char *endptr; // location to store end of parsing
errno = 0;
*f = strtof(buf, &endptr);
bool no_conversion = buf == endptr;
// Allow trailing white-space
while (isspace((unsigned char) *endptr)) {
endptr++;
}
if (no_conversion || *endptr) {
fputs("error: invalid input. Please enter a number\n", stderr);
continue; // No conversion or junk at the end
}
// Add additional tests if one wants to detect out-of-range (not shown)
....
Well as long as you want scanf to give you a float, you will never get the letter Q in your user-input. Also you don't have any control on filtering the input.
What you can do is get a string from scanf, and filter it yourself. You can write a function that checks if the string consists of just the characters you want to accept, and call it in your your condition. Something like this:
char input[128];
if (scanf("%s", input) != 1 && check_input(input))
Of course you will have to convert the input to a float yourself (maybe using atof() or strtof()), and do something with the Q. But you will have more control over what input you accept.

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

Turning my array into a function and making 2nd function to print it

Im trying to create a program that has 2 options, view and compute. Right now im trying to figure how to turn my array where i will input my values into a function, so i can go in and out several times to store the values. I also want to view to be a function where i can view the values several times.
i've managed to get the computing part to work in my main, now i need to turn it into a function. Secondly how do i create a second function to view it?
My code is a bit mess please bear with me.
#include <stdio.h>
#define LENGTH 10
int enterMeasurements();
int main(void)
{
char input;
int nrMeasurements=0;
int arrayTable[LENGTH] = {0};
//main menu
do
{
char input;
printf("\nMeasurement tool 1.0\n");
printf("V for (View)\n");
printf("E for (Enter Values)\n");
printf("C for (Compute Values)\n");
printf("R for (Reset Values)\n");
printf("Q for (Quit)\n");
printf("\nEnter input: ");
scanf(" %c", &input);
if(input == 'v' || input == 'V')
{
// function to print array values
printf("[ ]\n");
}
else if(input == 'e' || input == 'E')
{
// enter values here
nrMeasurements = enterMeasurements(arrayTable,nrMeasurements); // my function for entering values
}
else if (input == 'c' || input == 'C')
{
// enter function to calc min, max and avg and prints it.
printf("[ min max avg ]\n");
}
else if (input == 'r' || input == 'R')
{
// enter function that erase the entire array.
printf("[ erase array ]\n");
}
}
while (input !='q' && input != 'Q');
return 0;
}
int enterMeasurements()
{
int enterMeasurements(int arrayTable[], int nrMeasurements)
{
int i;
for (i = 0; i < LENGTH; i++)
{
printf("Enter Measurement #%i (or 0): ", i+1);
scanf("%d", &arrayTable[i]);
if (arrayTable[i] == 0 )
break;
}
return i;
}
To help you get started (you should really read a beginners book on the subject) I will show you the printArray function.
First of all the printArray function needs to know the actual array to print. it also needs to know the number of elements in the array. This can be accomplished in two ways: Global variables (which no-one is really recommending) or with function arguments.
You first need to tell the compiler that the function takes arguments:
void printArray(int *array, size_t numElements)
The above line tells the compiler that the printArray function takes two arguments: One which is called array and is a pointer to int (arrays "decays" to pointers to their first element when passed to functions), and one argument that is named numElements and is of type size_t (which is a good type for sizes and number of elements and similar things). The function is declared to return nothing with the void keyword.
The declared arguments can then be used inside the function like any other variables in the scope of the function, and are in fact just like any other local variable defined inside the function. So you can then use them like e.g.
void printArray(int *array, size_t numElements)
{
for (size_t i = 0; i < numElements; ++i)
{
printf("array[%d] = %d\n", i, array[i]);
}
}
To call this function you need to pass the array and the number of elements, much like you pass arguments to any other function like scanf or printf:
printArray(arrayTable, i);
Note that the function doesn't return anything, which means you can't use it in printf, or any other expression that expects a value.
You should of course also make your forward function prototype declaration match the actual function definition.
Alex, continuing from your last comment, to display a menu that will allow you to add values to your array, delete values from your array and view the array (along with the max, min and average of the values), can do something similar to the following. Note: the command line isn't a windowed user interface, so your menu operations are more like a printed receipt of your transactions with it. (you can do nice text windows and stationary menus, but that generally requires an text library, such as ncurses which is well beyond the scope of your question.
As explained in the comment, your basic approach is simply to create a loop that repeats continually. It will display your menu and allow you to enter your selection from a list, e.g.:
======== Program Menu =========
V) View the Array.
I) Insert New Value.
D) Delete Existing Value.
N) Display Minimum Value.
X) Display Maximum Value.
A) Display Average of Values.
S) Display Sum of Values.
Q) Quit.
Selection:
After the user enters the selection, to make the comparison easier, the user's input in converted to lower-case. Also note, that the input is read as a string using fgets (a line-oriented input function) which makes taking user input much easier than having to worry about whether the '\n' remains in the input buffer (stdin) just waiting to cause problems for your next input. (you can use the scanf family of functions, but YOU are the one responsible for accounting for each character entered by the user (and emptying the input buffer).
Reading input with fgets will read up to and including the '\n', so their is no chance of the '\n' being left unread in stdin. Also note that fgets will read a string or characters, where you are only interested in the first. That is easily handled simply by referencing the first character in the buffer. (e.g. if you are reading user-input into a buffer called buf, you can simply use buf[0] to access the first character, or simply *buf for that matter)
After the user input is read, the first character is passed to a switch statement, where each case of the statement is compared against the first character. If the character matches a case, then the actions associated with that case are taken, ending with the break (you can read about fall-through processing if the break is omitted on your own)
As mentioned in the comment, if you simply need to break out of one loop, then break is all you need. However here, your switch statement is inside the loop. A single break will only get you out of the switch but not the outside for loop. To break out of nested loops, use the goto statement. (you could also add more variables and set some type of exit flag, but why? This is what the goto was meant to do. (there are some cases where a flag is equally good as well)
In each case within the switch, you are free to call whatever code is needed to handle that menu selection. You will note we simply call short helper functions from within the switch to print the array, insert values, remove values, etc. You can put all the code in the switch if you like, it just rapidly become unreadable.
Putting that altogether, you can do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
enum { MAXN = 64 }; /* constant - max numbers of vals & chars */
void showmenu ();
void prnarray (int *a, int n);
int addvalue (int *a, int n, int newval);
int delvalue (int *a, int n, int index);
int minvalue (int *a, int n);
int maxvalue (int *a, int n);
int sumvalues (int *a, int n);
int main (void) {
int vals[MAXN] = { 21, 18, 32, 3, 9, 6, 16 }, /* the array */
n = 7;
for (;;) { /* loop until user quits or cancels, e.g. ctrl+d */
showmenu(); /* show the menu */
char buf[MAXN] = "";
fgets (buf, MAXN, stdin); /* read user input */
/* convert to lower-case for comparison of all entries */
switch (tolower (buf[0])) { /* 1st char is entry */
case 'v' : prnarray(vals, n);
break;
case 'i' : printf ("\n enter the new value: ");
if (fgets (buf, MAXN, stdin) &&
isdigit (buf[0])) {
n = addvalue (vals, n, atoi (buf));
}
break;
case 'd' : printf ("\n enter the index to delete: ");
if (fgets (buf, MAXN, stdin) &&
isdigit (buf[0])) {
n = delvalue (vals, n, atoi (buf));
}
break;
case 'n' : printf ("\n Mininum of '%d' values is : %d\n",
n, minvalue (vals, n));
break;
case 'x' : printf ("\n Maxinum of '%d' values is : %d\n",
n, maxvalue (vals, n));
break;
case 'a' : printf ("\n Average of '%d' values is : %.2lf\n",
n, (double)sumvalues (vals, n)/n);
break;
case 's' : printf ("\n Sum of '%d' values is : %d\n",
n, sumvalues (vals, n));
break;
case 'q' : printf (" that's all folks...\n");
goto done;
default : if (!buf[0]) { /* check for manual EOF */
putchar ('\n'); /* tidy up */
goto done;
}
fprintf (stderr, "error: invalid selection.\n");
}
}
done:; /* goto label - breaking 'for' and 'switch' */
return 0;
}
void showmenu ()
{
fprintf(stderr, "\n ======== Program Menu =========\n\n"
" V) View the Array.\n"
" I) Insert New Value.\n"
" D) Delete Existing Value.\n"
" N) Display Minimum Value.\n"
" X) Display Maximum Value.\n"
" A) Display Average of Values.\n"
" S) Display Sum of Values.\n"
"\n"
" Q) Quit.\n"
"\n"
" Selection: ");
}
void prnarray (int *a, int n)
{
int i;
printf ("\n there are '%d' values in the array:\n\n", n);
for (i = 0; i < n; i++)
printf (" array[%2d] : %d\n", i, a[i]);
}
int addvalue (int *a, int n, int newval)
{
if (n == MAXN) {
fprintf (stderr, "error: all '%d' values filled.\n", n);
return n;
}
a[n++] = newval;
return n;
}
int delvalue (int *a, int n, int index)
{
if (index < 0 || index > n - 1) {
fprintf (stderr, "error: index out of range.\n");
return n;
}
int i;
for (i = index + 1; i < n; i++)
a[i-1] = a[i];
a[i] = 0;
return --n;
}
int minvalue (int *a, int n)
{
int i, min = INT_MAX;
for (i = 0; i < n; i++)
if (a[i] < min)
min = a[i];
return min;
}
int maxvalue (int *a, int n)
{
int i, max = INT_MIN;
for (i = 0; i < n; i++)
if (a[i] > max)
max = a[i];
return max;
}
int sumvalues (int *a, int n)
{
int i, sum = 0;
for (i = 0; i < n; i++)
sum += a[i];
return sum;
}
(note: there are always additional validation checks you can add to test whether you have read all the input the user provided, etc.. But given the crux of your question I'll leave that learning to you)
Example Use/Output
$ ./bin/menusimple
======== Program Menu =========
V) View the Array.
I) Insert New Value.
D) Delete Existing Value.
N) Display Minimum Value.
X) Display Maximum Value.
A) Display Average of Values.
S) Display Sum of Values.
Q) Quit.
Selection: v
there are '7' values in the array:
array[ 0] : 21
array[ 1] : 18
array[ 2] : 32
array[ 3] : 3
array[ 4] : 9
array[ 5] : 6
array[ 6] : 16
======== Program Menu =========
V) View the Array.
I) Insert New Value.
D) Delete Existing Value.
N) Display Minimum Value.
X) Display Maximum Value.
A) Display Average of Values.
S) Display Sum of Values.
Q) Quit.
Selection: i
enter the new value: 77
======== Program Menu =========
V) View the Array.
I) Insert New Value.
D) Delete Existing Value.
N) Display Minimum Value.
X) Display Maximum Value.
A) Display Average of Values.
S) Display Sum of Values.
Q) Quit.
Selection: v
there are '8' values in the array:
array[ 0] : 21
array[ 1] : 18
array[ 2] : 32
array[ 3] : 3
array[ 4] : 9
array[ 5] : 6
array[ 6] : 16
array[ 7] : 77
Look things over and let me know if you have any questions. Also, as you are just learning, make sure you are compiling your code with compiler warnings enabled and that you don't consider your code reliable until it compiles without warning. That means you should be compiling with at least the -Wall -Wextra flags set. If you are using gcc and the command line, then it would be:
gcc -Wall -Wextra -O2 -o simplemenu simplemenu.c
To compile the code in simplemenu.c into an executable named simplemenu with the -O2 optimizations applied. If you are really wanting to eliminate all warnings, add -pedantic as well. For codeblock or other IDE, look through the compiler menu options, they all provide a place to input all of the options you would like. Good luck with your code.
Procedural Approach Without Functions & Input With scanf
OK, now that we know how far we need to backup, let's look at rewriting the code in a bare minimum, top-down approach without using functions and taking user input with scanf (which will cause you more grief, especially taking mixed character and numeric input, but it can be done, if you account for the '\n' left in stdin)
First a note about taking input with scanf. When you ask for user input, like with the menu selection, and the user enters V and presses Enter, the input buffer stdin contains "V\n" (the '\n' as the result of pressing Enter). When you then use scanf to read a character (e.g. char sel; scanf ("%c", &sel);) the 'V' is taken from stdin and stored in sel leaving '\n' in stdin. If you then attempt to read another character (e.g. char nextch; scanf ("%c", &nextch);) it will appear that scanf has skipped reading nextch because it never allow you to enter a value. What has actually happened is scanf ("%c", &nextch); has read the '\n' that remained in stdin as your next character and is quite content with the value of 0xa (10 decimal) in nextch.
You must always account for the '\n' when using scanf. You have two options, 1) leave a space before the conversion specifier (e.g. scanf (" %c", &nextch); or 2) use the assignment suppression operator '*' (e.g. scanf ("%c%*c", &nextch);), the second %*c telling scanf to read and discard the following character without adding the conversion to the match count (which is the integer value returned by scanf). Which brings up the most important point of all, always check the return of scanf. (otherwise, you have no clue whether you have an actual value to work with or just garbage) I will leave the reading of man scanf to you for further details on the effect of the space before the conversion specifier, and the assignment suppression operator.
The return for scanf (the match count) is the number of successful conversions performed based on the number of conversion specifiers contained within the format string (e.g. scanf (" %c %d", &somechar, &someint); contains 2 conversion specifiers %c and %d, so the return for scanf after successful conversion of both would be 2. If a matching or conversion failure occurs, the return will be less than 2 and if an error condition is encountered reading from the stream (stdin in this case) EOF is returned (generally a value of -1) All of this, and more, is why scanf is NOT the preferred method for taking user input in C. (that being said, it is what most tutorials, and most teachers, make the poor choice to expose new C programmers to without an understanding of the pitfalls)
With that out of the way, if you work through the example below, you will see that I have simply moved the code from the functions to within the if ... else if ... else framework. (look at the one-to-one relationship from where I call functions from the switch in the first example and the code below) This should also show why breaking the code up into logical functions, improves readability and improves code re-use. Compare the use of the switch statement with the if ... else if ... else daisy chain. Both are fine, but to me, the switch is more easily readable at a glance.
You should make sure you understand both versions as they are both basic entry level approaches to using C. Take your time going through each and if you have questions that you cannot answer by consulting one of the references provided in the tag-wiki link, just ask.
#include <stdio.h>
#include <stdlib.h> /* for atoi */
#include <limits.h> /* for INT_MIN/INT_MAX */
enum { MAXN = 64 }; /* constant - max numbers of vals & chars */
int main (void) {
int vals[MAXN] = { 21, 18, 32, 3, 9, 6, 16 }, /* the array */
n = 7;
for (;;) {
char c;
/* show the menu */
fprintf(stderr, "\n ======== Program Menu =========\n\n"
" V) View the Array.\n"
" I) Insert New Value.\n"
" D) Delete Existing Value.\n"
" N) Display Minimum Value.\n"
" X) Display Maximum Value.\n"
" S) Display Sum of Values.\n"
" A) Display Average of Values.\n"
"\n"
" Q) Quit.\n"
"\n"
" Selection: ");
/* read selection (inside of if is OK), check EOF or quit */
if (scanf (" %c", &c) == EOF || c == 'q' || c == 'Q') {
printf ("\n that's all folks...\n");
break;
}
if (c == 'v' || c == 'V') { /* view array code */
printf ("\n there are '%d' values in the array:\n\n", n);
int i;
for (i = 0; i < n; i++)
printf (" array[%2d] : %d\n", i, vals[i]);
}
else if (c == 'i' || c == 'I') { /* insert value code */
if (n == MAXN) {
fprintf (stderr, "error: all '%d' values filled.\n", n);
continue;
}
int newval = 0;
printf ("\n enter the new value: ");
if (scanf (" %d", &newval) == 1) {
vals[n] = newval;
n++;
}
else
fprintf (stderr, "error: invalid input.\n");
}
else if (c == 'd' || c == 'D') { /* delete value code */
int i, index = 0;
printf ("\n enter the index to delete: ");
if (scanf (" %d", &index) != 1) {
fprintf (stderr, "error: invalid input.\n");
continue;
}
if (index < 0 || index > n - 1) {
fprintf (stderr, "error: index out of range.\n");
continue;
}
for (i = index + 1; i < n; i++)
vals[i-1] = vals[i];
vals[i] = 0;
n--;
}
else if (c == 'n' || c == 'N') { /* display minimum code */
int i, min = INT_MAX;
for (i = 0; i < n; i++)
if (vals[i] < min)
min = vals[i];
printf ("\n Mininum of '%d' values is : %d\n",
n, min);
}
else if (c == 'x' || c == 'X') { /* display maximum code */
int i, max = INT_MIN;
for (i = 0; i < n; i++)
if (vals[i] > max)
max = vals[i];
printf ("\n Maxinum of '%d' values is : %d\n",
n, max);
}
else if (c == 's' || c == 'S') { /* compute sum code */
int i, sum = 0;
for (i = 0; i < n; i++)
sum += vals[i];
printf ("\n Sum of '%d' values is : %d\n",
n, sum);
}
else if (c == 'a' || c == 'A') { /* compute avg code */
int i, sum = 0;
double avg = 0.0;
for (i = 0; i < n; i++)
sum += vals[i];
avg = (double)sum/n;
printf ("\n Average of '%d' values is : %.2lf\n",
n, avg);
}
else /* if not matched, then invalid selection */
fprintf (stderr, "error: invalid selection.\n");
}
return 0;
}
(the operation and output from both program versions will be identical)

C Basics : Grade Book Using Array Storing and Printing, Plus Looping

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

Resources