not taking input in c - c

char name[20];
printf("What is your name : ");
scanf("%s",name);
char user_grade;
printf("\nEnter your grade");
scanf("%c", user_grade);
switch(user_grade){
case 'A':
printf("%s you have passed the exams with great grade !",name);
break;
case 'B':
printf("%s you have passed the exams with good grades!",name);
break;
case 'C':
printf("%s you have passed the exams with nice grades!",name);
break;
case 'F':
printf("%s sorry you have failed the exam..");
break;
default:
printf("sorry your grade are invalid. retry again by typing valid grades");
break;
}
So the code has to ask the user their name and grade and check if they have passed the exams or not
but the code didn't take the grade input and outputs the result

Try scanf(" %c", &user_grade);
Two changes, put a space before %c so it will discard any leading whitespace, and pass the address of a variable with & so the function can change the variable.
scanf expects a pointer for an argument for each item of input. name is a pointer because you declared it as an array (although you're probably getting a compiler warning there).

Mainly the issue is that you passed user_grade in scanf("%c", user_grade); by value. You need to pass it by reference, using &: scanf("%c", &user_grade);
char name[20];, are you sure the name will be under 20 characters long? What if someone entered a name longer than 20 characters?:
scanf("%s",name); will just write it to the array irrespective of the size of the array, and your program will crash. So, use fgets(name, sizeof name, stdin);. But there is an issue here, fgets() also returns the newline character 0x0A/\n, so you need to remove it using name[strcspn(name, "\n")] = '\0';.
A fflush(stdin); after fgets() might be necessary in some situations here.
Next, you need to pass the variable user_grade by reference to scanf(), since it needs to memory address of user_grade to write to it. Use scanf(" %c", &user_grade);.
Here: CREDIT GOES TO #littleadv: to discard leading whitespace use: scanf(" %c", &user_grade);
For all of the printf() calls inside the switch, its best to add a \n to the end of each string to print on screen.
So, your final code would be:
char name[20] = {}; // initialize to be safe
printf("What is your name : ");
if (fgets(name, sizeof name, stdin) == NULL) {
// check for errors
puts("ERROR reading input!");
exit(1);
}
name[strcspn(name, "\n")] = '\0'; // needs <string.h>
char user_grade = '0'; // initialized to 0, maybe not needed; I would be on the safe side
printf("\nEnter your grade: ");
scanf(" %c", &user_grade); // pass by reference using `&`
switch (user_grade) {
case 'A':
printf("%s you have passed the exams with great grade !\n", name);
break;
case 'B':
printf("%s you have passed the exams with good grades!\n", name);
break;
case 'C':
printf("%s you have passed the exams with nice grades!\n", name);
break;
case 'F':
printf("%s sorry you have failed the exam..\n", name);
break;
default:
printf("sorry your grade are invalid. retry again by typing valid grades\n");
break;
}
Also, you could use switch (tolower(user_grade)) { (tolower() from <ctype.h>) to allow user to enter lowercase grades like a and still be accepted.
Edit:
#DavidC.Rankin suggested that I use fgets instead of scanf to avoid \n being left over and causing problems in loops, so here we can use this instead of scanf(" %c", &user_grade);:
char user_grade[1024] = {};
fgets(user_grade, 1024, stdin);
for (int i = 0; i < strlen(user_grade); i++) {
if (user_grade[i] != ' ' && user_grade[i] != '\t') {
// do the switch here
break;
}
}

Related

C language - Menu issue with multiple void statements and do-switch-case

I am trying to make a menu with the options to compress a text inputted by the user and then store that value to be extracted in the extract menu option.
The issue lies in that it seems like the code isn't following the void statements, for example
case 1: compress();//compress statement
It seems to only get the printf statement in the void compress(void) and not the scanf, which it then follows with the loop of the menu.
Any solutions?
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
void menu(void);
void compress(void);
void extract(void);
int main(void)
{
menu();
return 0;
}
void menu(void)
{
int choice;
do
{
printf("Menu\n\n");
printf("1. Compress Text\n");
printf("2. Extract Text\n");
printf("3. Exit\n");
scanf_s("%d", &choice);
switch (choice)
{
case 1: compress();//compress statement
break;
case 2: extract();//extract statement
break;
case 3: printf("Ciao\n");
exit(0);
break;
default: printf("Invalid Entry. Try again\n");
break;
}
} while (choice != 3);
}
void compress(void) {
printf("\n-------------------------\n");
printf(" COMPRESS ");
printf("\n-------------------------\n");
printf("\nPlease enter a word/sentence to be compressed:\n");
char txt[200];
scanf_s("%c", &txt);
printf("\nYour word/sentence is %c", txt, "\n");
char comp = strlen(txt);
int mask[200]{};
for (int i = 0; i < comp; ++i) //loop until all leters are masked
{
mask[i] = i + 127;
printf("\nYour compressed word/sentence is %c ", mask[i]);
}
return;
}
void extract(void) {
printf("\n-------------------------\n");
printf(" EXTRACT ");
printf("\n-------------------------\n");
return;
}
You are scanning only one single character, and as "%c" doesn't skip white-space this is the newline character terminating the previous input.
You instead want to read in a string, and to be on the safe side you should add the maximum length to read to: "%199s" (note: one less than array size to leave space for the terminating null character):
scanf_s("%199s", txt);
Note, too, that as txt is an array it decays to a pointer automatically when being passed to a function (see above); taking the address of (&txt) produces a pointer with the same value, but of a different type: char(*)[200]. This pointer is not compatible to neither %c nor %s format specifier, thus you actually produce *undefined behaviour!
Note, too, that scanf_s (any function from scanf family) will stop reading at the first whitespace – a sentence might, though contain multiple words separated by whitespace. You'd just read the first one of them, though. So you might want to drop scanf for this input entirely in favour of e.g. fgets:
fgets(txt, sizeof(txt), stdin);
Note, here, too, that the previous scanf("%d", ...) did not consume the terminating newline, so you'll need to ignore that, e.g. by a preceding call to getchar.
Crediting this last point to Jonathan Leffler who hinted to in his comment to the question)

Infinite loop Do While

I am trying to create a program, in which at the beginning it shows a menu to the user, which consists in a do{ ... }while; which reads an int., with a switch inside.
It works perfectly to read and check the integer, the problem is when writing a character or string, which gets stuck in an infinite loop showing the default message of the switch loop. The code is as follows:
int op;
printf("Choose an option:\n 1. option 1\n 2. option 2\n 3. option 3\n");
do{
scanf("%d", &op);
switch(op){
case 1: (instruction); break;
case 2: (instruction); break;
case 3: (instruction); break;
default: printf("\nPlease enter a valid option\n");
}
}while(op<1 || op>3);
It works perfectly to read and check the integer, the problem is when writing a character or string, which gets stuck in an infinite loop showing the default message of the switch loop.
scanf("%d", &op); does nothing when the input is not a valid int, you need to check the return value is 1 and if not to decide what to do like for instance read a string or flush up to the end of line, also managing the EOF case
Note in case scanf does nothing op is not set
So can be :
int op;
printf("Choose an option:\n 1. option 1\n 2. option 2\n 3. option 3\n");
do{
if (scanf("%d", &op) != 1) {
// flush all the line
while ((op = getchar()) != '\n') {
if (c == EOF) {
puts("EOF, abort");
exit(0); /* what you want */
}
}
op = -1; /* invalid value */
}
switch(op){
case 1: (instruction); break;
case 2: (instruction); break;
case 3: (instruction); break;
default: puts("\nPlease enter a valid option");
}
}
}while(op<1 || op>3);
I encourage you to never trust on an input and always check return value of scanfetc
The %d conversion specifier is seeking for decimal input only. It does not work to consume characters or strings. If you input a character instead of an decimal value, the directive will fail, and because op isn´t initialized you have undefined behavior.
To catch a string with scanf() use the %s conversion specifier or use fgets(). For only catching one character use the %c conversion specifier with scanf().

Switch case 0: entering 0 and entering a letter

int DisplaySchedule()
{
int nDisplaySchedule_Choice;
system("cls");
printf("----- DISPLAY SCHEDULE -----\n");
printf("Pick departure station\n\t");
printf("[1] San Pedro\n\t");
printf("[2] Santa Rosa\n\t");
printf("[3] Calamba\n\n\t");
printf("[9] Go Back\n\t");
printf("[0] Exit\n\n");
printf("Choice: ");
scanf("%d", &nDisplaySchedule_Choice);
printf("\n");
switch (nDisplaySchedule_Choice) {
case 1: SanPedro(); break;
case 2: SantaRosa(); break;
case 3: Calamba(); break;
case 9: OpeningScreen(); break;
case 0: printf("Summary()"); break;
default:
printf("ERROR. INPUT A VALID RESPONSE.\n\n");
DisplaySchedule();
break;
}
return;
}
I have this code in which when I enter a letter, instead of printing the error message, it prints case 0: instead. Is there any way for me to make it so that case 0: will only function if and only if I enter "0" in the scanf statement?
You have undefined behaviour here.
scanf, when scanning for int (%d), fails because you input a character - due to matching failure. Thus not reading anything into nDisplaySchedule_Choice at all.
Since nDisplaySchedule_Choice is uninitialized to start with, it just happens to have 0 and thus goes to case 0.
The solution is to check the scanf return value before proceeding to use nDisplaySchedule_Choice. scanf returns the number of items successfully scanned.
If scanf fails to read a value (for instance because you told it to read an int and gave it a letter) it won't change your variable. So nDisplaySchedule_Choice won't change in a way that you can check in your switch. At least not if you don't initialize it - you can however set it to a value that is not covered by your switch, and if it didn't change, you know that scanf failed to read a value.
Or you could check the return value of scanf to see if it managed to read a value:
int result = scanf("%d", &nDisplaySchedule_Choice);
if (result == 0) {
int c;
while ((c = getchar()) != '\n' && c != EOF); // flush the invalid input
printf("ERROR. INPUT A VALID RESPONSE.\n\n");
DisplaySchedule();
}
else switch ...

comparing strings and printing stored strings in C

I am having trouble getting this program to print the strings I enter properly. It keeps telling me that I have not entered data, even when I have. I also can't get the strings to compare to run my if statement. Thank for any help.
#include <stdio.h>
//function prototype
void enterPerson();
void enterChoice();
//global variables
char person[30];
char choice;
int main(void) {
enterPerson();
enterChoice();
printf("Please try the Precipitation Program again.\n");
return 0;
}
void enterPerson(){
// Ask for person name
printf("Please enter name:\n");
scanf("%s", &person);
//-------------------------------------------
printf("person is %s\n", person);
//-------------------------------------------
}
void enterChoice(){
//initialize choice
choice = "M";
//ask what they choose
printf("Do you choose test or rate? (Enter T for test R for rate)\n");
scanf("%c", &choice);
printf("Xchoice is: %c\n", choice);
if ((choice == 'T')||(choice == 'R')){
printf("choice is: %c\n", choice);
}
else{
printf("Incorrect or no data was input at this time\n");
}
}
As mentioned in comments, there are at least 3 problems:
scanf("%s", person); - do not take the address of char array.
scanf(" %c", &choice); - insert space to ignore whitespace.
choice = 'M'; - "M" is a string literal, while choice is char.
There is a linefeed (0xa) character left in the input buffer. You can see it by printing the choice variable after your scanf line with:
scanf("%c", &choice);
printf("c: %x\n", choice);
There are several options to get rid of this. Easiest is explained here.
Also there is a problem in:
scanf("%s", &person);
Character array name in C points to the first character, so you should fix this with:
scanf("%s", person);

"scanf with printf" vs "fgets with printf"

I know about the difference and the advantage/disatvantage of using scanf and fgets.
I don't understand the relations between printf and this two C standard functions.
I have this simple code:
void print_choice(char * list, char * choice)
{
/* check parameters */
if(!list || !choice)
return;
printf("list of users: %s\n", list);
printf("Choice -- ? ");
/* scanf("%s", &choice); */
/* fgets(choice, 20, stdin); */
}
int main()
{
char choice[20];
char * list = "marco:dario:roberto:franco";
print_choice(list, choice);
printf("choice = %s\n", choice);
return 0;
}
if I use fgets, printf print the result correctly on stdout;
If I use scanf, printf` doesn't print anything on stdout.
Why this behaviour?
You used scanf("%s", &choice); which passes a char ** to scanf() when it expects a char *.
Drop the &.
If your compiler wasn't complaining, you either haven't turned on enough warnings or you need a better compiler.
Change
scanf("%s", &choice);
to
scanf("%s", choice);
you have to use
scanf("%s", choice);
instead of
scanf("%s", &choice);
Changing this scanf("%s", &choice); to this scanf("%s", choice); will cause scanf and fgets to show almost similar behavior.
scanf requires an address as an argument. It goes on and stores the input from stdin after converting it based on the supplied format specifier. Here the format specifier is %s so it will try to store the input at the address pointed by address of choice . What you need here is the address from where the choice array will begin,which in this case is choice itself.

Resources