Why does my nested if condition is not working properly in c? - arrays

Here is my assignment:
Ask the user to input their name.
If their name ends with a vowel, ask them to input their age as well.
If their age is even, print Wow, you're special!. Otherwise, if their age is odd, ask them to input their birth year.
If their birth year is even, print Oh, you're still special!. Otherwise, print You will be special next year..
If their name ends with a consonant though, print You're awesome!
Here is my code:
#include <stdio.h>
#include <string.h>
int main() {
// variables
char name[100];
int age, birth;
printf("Enter name: ");
scanf("%[^\n]s", &name);
// formula for getting last letter of the name
int size = strlen(name);
int last = size - 1;
if (name[last] == 'a' || name[last] == 'e' || name[last] == 'i' ||
name[last] == 'o' || name[last] == 'u') {
printf("Enter age: ");
scanf("%d", &age);
if (age %2 == 0) {
printf("Wow, you're special!\n");
} else {
printf("Enter birth year: ");
scanf("%d", &birth);
}
if (birth % 2 != 0) {
printf("You will be special next year.\n");
} else {
printf("Oh, you're still special!\n");
}
} else {
printf("You're awesome!\n");
} // end of nested if
return 0;
}
The problem is with the birth variable: the age and birth variables seems to be connected, because when the condition of age executes the condition of birth executes as well leading to a multiple executions of conditions.

"%[^\n]s" is a typo. %[ and %s are two separate scanf specifiers - you do not combine them as such. What you have is a %[ specifier that reads characters until a newline, and then the format string attempts to match a literal s character.
An unbound %[ is as dangerous as gets. You most provide a maximum field-width specifier that is at most the size of your array minus one (e.g., %99[^\n]) to limit the amount of data scanf reads, and prevent a buffer overflow.
&name would be of type char (*)[100], that is a pointer to an array. %[ expects a char *, simply a pointer to char. Passing an array to a function causes it to decay to a pointer to its first element - you do not need the & operator here.
You must test the return value of scanf is the expected number of conversions, otherwise you are operating blindly, and may utilize uninitialized or otherwise indeterminate values.
You should initialize birth, otherwise its value is indeterminate. In the event that age is even, you would attempt to use this indeterminate value in birth % 2 != 0.
Failing to correct most of the above issues will lead to problems, including Undefined Behaviour.
A safer example program to study:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char name[100];
int age;
int birth = 0;
printf("Enter name: ");
if (1 != scanf("%99[^\n]", name)) {
fprintf(stderr, "Could not read <name>.\n");
return EXIT_FAILURE;
}
size_t length = strlen(name);
if (!length) {
fprintf(stderr, "<name> is empty!\n");
return EXIT_FAILURE;
}
if (!strchr("aeiou", name[length - 1])) {
puts("Name does not end in a vowel.");
return EXIT_SUCCESS;
}
printf("Enter age: ");
if (1 != scanf("%d", &age)) {
fprintf(stderr, "Could not read <age>.\n");
return EXIT_FAILURE;
}
if (age % 2 != 0) {
printf("Enter birth: ");
if (1 != scanf("%d", &birth)) {
fprintf(stderr, "Could not read <birth>.\n");
return EXIT_FAILURE;
}
}
printf("NAME: <%s> AGE: <%d> BIRTH: <%d>\n", name, age, birth);
}

The conditions expressed in the assignment imply that you only test the birth year if the age is even... otherwise you do not even ask for the birth age. Your tests are not properly nested.
Note that scanf("%[^\n]s", &name); is incorrect:
you must tell scanf() the maximum number of characters to store to the destination array to avoid a potential buffer overflow;
&name is not the expected type, you should just pass name, which is an array that is converted automatically to a pointer to its first element when passed as an argument or used in an expression (except as an argument to sizeof or alignof);
the s in the format is meaningless. Use scanf(" %99[^\n]", name) instead;
test the return value to detect invalid or missing input and avoid undefined behavior later in the program.
Also note that letters can be uppercase and the last character may be neither a vowel nor a consonant (eg: X Æ A-12).
Here is a modified version, more consistent with the assignment:
#include <stdio.h>
#include <string.h>
int main() {
// variables
char name[100];
int age, birth;
printf("Enter name: ");
// read up to 99 characters, skipping initial white space
if (scanf(" %99[^\n]", name) != 1) {
fprintf(stderr, "invalid or missing input for name\n");
return 1;
}
// formula for getting last letter of the name
size_t len = strlen(name);
if (len == 0) {
fprintf(stderr, "invalid empty name\n");
return 1;
}
char last = name[len - 1];
// test if last character is a vowel (trailing 'y' is a vowel)
if (strchr("aeiouyAEIOUY", last)) {
printf("Enter age: ");
if (scanf("%d", &age) != 1) {
fprintf(stderr, "invalid or missing input for age\n");
return 1;
}
if (age % 2 == 0) {
printf("Wow, you're special!\n");
} else {
printf("Enter birth year: ");
if (scanf("%d", &birth) != 1) {
fprintf(stderr, "invalid or missing input for birth\n");
return 1;
}
if (birth % 2 == 0) {
printf("Oh, you're still special!\n");
} else {
printf("You will be special next year.\n");
}
}
} else {
// the last character is not a vowel, test if it is a consonant
if (strchr("bcdfghjklmnprstvwxzBCDFGHJKLMNPQRSTVWXZ", last) {
printf("You're awesome!\n");
}
}
return 0;
}

of course you get two outputs, your if statements regarding age and birth are parallel. Shouldn't your birth block be nested inside of the else block for age?

Related

Getting an error stating "Run-time check failure #2 - stack around the variable 'rejected' was corrupted. (Visual Studio)

All of my code works great until I press the key to exit my program, which happens when I press'-'. I then get that error and I am not sure how to resolve it. Here is my code:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <ctype.h>
int main() {
char rejected[2] = { 0 };
char input = 0;
char exitProgram = 0;
printf("Please enter three letters you want filtered\n");
for (int x = 0; x <= 2; x++) {
scanf("%s", &rejected[x]);
}
printf("Please enter a letter and I will tell you if it is filtered or not\n");
for (int y = 0; y <= 99; y++) {
scanf("%s", &input);
if (input == rejected[0] || input == rejected[1] || input == rejected[2]) {
printf("Filtered letter!\n");
}
else {
printf("Okay letter!\n");
}
if (y %5 == 0) {
printf("If you would like to exit this program, please enter '-'. If not, please enter '+'\n");
scanf("%s", &exitProgram);
if (exitProgram == '+') {
printf("Okay, continue having fun with my program!\n");
}
else if (exitProgram == '-') {
printf("Thank you for playing with my program!\n");
break;
}
}
}
return 0;
}
You're using scanf in three places with the %s format specifier, which is used to read a string, and in each of those places it is the incorrect one. Using the wrong format specifier invokes undefined behavior which can cause a crash.
First:
scanf("%s", &rejected[x]);
Here you're really want to read a single character, so you need the %c format specifier. This one accepts any character, including spaces and newlines, so you'll want to precede it with a space to consume any leftover newlines from the prior read:
scanf(" %c", &rejected[x]);
Second:
scanf("%s", &input);
input is an char, so you again want %c here:
scanf(" %c", &input);
Third:
scanf("%s", &exitProgram);
You're reading a character here, so use %c with a leading space as before:
scanf(" %c", &exitProgram);
You also have a problem here:
char rejected[2] = { 0 };
...
for (int x = 0; x <= 2; x++) {
scanf("%s", &rejected[x]);
}
The array rejected only contains 2 elements but you try to read in 3. This also invokes undefined behavior by writing past the end of the array. Change the size of the array to 3.
char rejected[3] = { 0 };

How to read multiple scanf input in C in one line? say 200 Merck 58.9?

I am new to this, please forgive me if my question has issues.
I would like to read in an integer, a character array (say size 30), and a floating point number in C. How do I read all of it in one line in C, say 200 Merck 58.9? I have tried using scanf consecutively in the following, but the second scanf is not working - the space between input (e.g. 200 Merck 58.9) is causing mayhem.
printf("Please enter an integer, one word string, and a float in one line");
scanf("%i", &any_integer);
scanf("%29s", &any_string); // no spaces - just a one word string
scanf("%f", &any_float);
I would then like to test what the user enters, ensure that the integer and float is positive and not above 50, and in the string ignore anything that is not a character. Here is a start for the integer.
/* Test the input */
while(any_integer < 0 || any_integer > 50)
{
printf("\n*** Invalid integer.\n Please enter a value between 1 and 50:
");
scanf("%i", &any_integer);
}/* end while */
while(any_float < 0)
{
printf("\n*** Invalid float.\n Please enter a positive value: ");
scanf("%f", &any_float);
}/* end while */
I would like to put this setup in an infinite while loop that terminates with 999, but i am struggling with the details laid out above.
Perhaps
char s[30];
int i;
float f;
while (true) {
if (scanf(" %d%29s%f", &i, s, &f) == 3) {
printf("%d%s%F", i, s, f);
} else{
// Sort out incorrect input
// Eat up to the new line
scanf("%*[^\n]");
if (feof(stdin)) {
// Do summat here - break perhaps
}
}
}
is what you require
The problem is you pass the address of any_string, which is incorrect, and may cause undefined behavior is any_string is a pointer to a char array. You should write:
scanf("%29s", any_string);
To improve error handling and improve input control, I suggest you read the line with fgets() and attempt parse with sscanf(). If the format is incorrect, you can restart with a new line:
int i1;
char s1[30];
float f1;
printf("Please enter an integer, one word string, and a float in one line\n");
for (;;) {
char buf[100];
char c;
if (!fgets(buf, sizeof buf, stdin)) {
fprintf(stderr, "premature end of file\n");
exit(1);
}
if (sscanf(buf, "%i%29s%f%c", &i1, s1, &f1, &c) != 4 || c != '\n') {
printf("Invalid input, enter a new line\n");
continue;
}
if (i1 < 0 || i1 > 50 || f1 < 0 || f1 > 50) {
printf("Invalid values: must be between 0 and 50, try again\n");
continue;
}
/* input values are correct */
break;
}
You can just put them in one line like that:
scanf("%i %29s %f", &any_integer, any_string, &any_float);

How to prevent user from entering more or less input than required in C?

I want the user to enter just two integers and not more than two or less than two. Also, upon invalid input, I wish to print an error and prompt the user to enter the two integers again. The user should enter two integers delimited by space and not a newline. Thus, for example:
1) Valid input is: 1 2
2) Invalid input: 1
3) Invalid input: 1 2 3
I have tried it with following two approaches:
#include<stdio.h>
int main(){
int first;
int second;
printf("Enter input:\n");
int returnValue = scanf("%d %d", &first, &second);
while(returnValue != 2){
printf("Invalid input. Please enter again: \n");
returnValue = scanf("%d %d", &first, &second);
}
printf("First: %d Second: %d\n", first, second);
return 0;
}
In this first approach involving scanf, I am unable to prevent the user from entering each integer on newline. Neither am I able to limit the input to just 2 numbers. That is, if the user inputs more than 2 integers, then program is accepting first 2 integers and ignoring third. I want to print error in that case.
My other approach involves fgets and sscanf:
#include<stdio.h>
int main(){
int first;
int second;
printf("Enter input:\n");
char line[20];
fgets(line, sizeof(line), stdin);
int returnValue = sscanf(line, "%d %d", &first, &second);
while(returnValue != 2){
printf("Invalid input. Please enter again: \n");
fgets(line, sizeof(line), stdin);
returnValue = sscanf(line, "%d %d", &first, &second);
}
printf("First: %d Second: %d\n", first, second);
return 0;
}
In this approach, I am able to print error if the user hits enter after entering just one integer. But I am unable to limit the input to just 2 numbers. That is, if the user inputs more than 2 integers, then program is accepting first 2 integers and ignoring third. I want to print error in that case.
So my question is, are my requirements achievable by modifying first approach as well as second approach?
Thanks.
One solution would be to use the %n conversion specification after the two %d conversions. The %n conversion specification does not match any characters, but stores the number of characters read to this point in the format string. So, in the call:
sscanf(line, "%d %d %n", &first, &second, &bufPos);
if the second %d is reached, then bufPos will hold the index of the character after the last character read in line. Since there is a space before the %n, zero or more white-space characters will be read and skipped over before the index value is stored in bufPos. Thus, after a valid entry, bufPos will indicate the \0 terminator. If any other character is found in line at this index, there were extraneous characters in the input.
Here is a modified version of your second code example. After fgets() reads a line of input, sscanf() is used to scan the string. If fewer than 2 matches are made, or if line[bufPos] is not '\0', then badInput is set to true. The input loop is a do loop that executes once, and continues to execute so long as badInput is true.
#include <stdio.h>
#include <stdlib.h> // for exit()
#include <stdbool.h> // for bool type
#define BUF_SIZE 100
int main(void)
{
int first;
int second;
char line[BUF_SIZE];
int returnValue;
int bufPos;
bool badInput = false;
do {
if (badInput) {
printf("Invalid input. Please enter again: ");
badInput = false;
} else {
printf("Enter input: ");
}
if (fgets(line, sizeof(line), stdin) == NULL) {
perror("Error in fgets()");
exit(EXIT_FAILURE);
}
returnValue = sscanf(line, "%d %d %n", &first, &second, &bufPos);
if (returnValue < 2 || line[bufPos] != '\0') {
badInput = true;
}
} while (badInput);
printf("First: %d Second: %d\n", first, second);
return 0;
}
Sample interaction:
Enter input: 1
Invalid input. Please enter again: 1 2 3
Invalid input. Please enter again:
Invalid input. Please enter again: 1 2
First: 1 Second: 2
To prevent issues when asking for char * you can use the regular expression.
If you are not forced to get two in one scanfyou could use this function:
int secure_input(int max, int min) {
int choice,buffer;
do {
choice = -1;//initialize in a values not included among min and max
scanf("%d", &choice);
while ((buffer = getchar()) != '\n' ? buffer != EOF : false); // empty the buffer to avoid infinite loop
} while (choice > max ? true : choice < min);
return choice;
}
In your main function you just to call the function like that:
first = secure_input(2;1);
Different from other answers, you could also parse the input using strtok(), then checking how many numbers were found. This approach is complicated, but it does offer a different outlook on the problem.
Inside your while() loop, you can check how many spaced numbers were found from fgets(), then if only 2 were found, then you can break out of the loop. Otherwise, keep searching. Once out of the loop, then you can just sscanf() two integers from the most recent input read. You can also use strtol() to check if integers are valid.
Note: strtok() is reeantrant, and it does modify the string that it parses. So in this case, you might need to create a copy of it somewhere. You can use strdup() or malloc() to do this.
Here is some example code that shows this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINESIZE 20
#define BASE 10
int main(void) {
char line[LINESIZE];
const int n = LINESIZE;
char *number, *copy, *endptr;
const char *delim = " ";
int first, second, check, invalidnum;
size_t slen, count;
while (1) {
printf("Enter input: ");
if (fgets(line, n, stdin) == NULL) {
printf("Error reading buffer from fgets()\n");
exit(EXIT_FAILURE);
}
slen = strlen(line);
if (slen > 0 && line[slen-1] == '\n') {
line[slen-1] = '\0';
} else {
printf("Buffer overflow detected\n");
exit(EXIT_FAILURE);
}
copy = strdup(line);
count = 0;
invalidnum = 0;
number = strtok(copy, delim);
while (number != NULL) {
check = strtol(number, &endptr, BASE);
if (endptr == number || check == 0) {
invalidnum = 1;
break;
}
count++;
number = strtok(NULL, delim);
}
free(copy);
copy = NULL;
if (count != 2 || invalidnum) {
printf("Invalid input\n\n");
} else {
break;
}
}
if (sscanf(line, "%d %d", &first, &second) != 2) {
printf("Unexpected error from sscanf()\n");
exit(EXIT_FAILURE);
}
printf("first = %d, second = %d\n", first, second);
return 0;
}
This is just another approach to your problem. In terms of simplicity, #David Bowling has the better idea, and I would suggest using his.

Difficulty using scanf for input

Can someone help me to solve my problem? I have a problem with %[^\n]. When I try to enter a false input the program loop the warning that I wrote, but if I use %s and I enter my string the next statement is not working properly.
#pragma warning (disable:4996)
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(){
char name[30];
char number[12];
int flag, flag1, flag2, flag3;
int i;
printf("Add New Contact\n");
do {
printf("input name [1..30 char]: ");
scanf("%[^\n]", name); fflush(stdin);
if ((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z')) {
flag = 1;
}
else {
flag = 0;
printf("First letter of name should be an alphabet (A-Z or a-z)\n");
}
if (strlen(name) > 30) {
flag1 = 0;
printf("Length of name should be between 1 and 30 characters\n");
}
else {
flag1 = 1;
}
} while (flag == 0 || flag1 == 0);
do {
printf("Input phone number[6..12 digits]: ");
scanf("%s", number); fflush(stdin);
for (i = 0; i < strlen(number); i++) {
if (number[i] >= '0' && number[i] <= '9') {
flag2 = 1;
}
else {
flag2 = 0;
}
}
if (flag2 == 0) {
printf("Phone numbers should only contain digits (0-9)\n");
}
if (strlen(number) >= 6 && strlen(number) <= 12) {
flag3 = 1;
}
else {
flag3 = 0;
printf("Length of phone numbers should be between 6 and 12 digits\n");
}
} while (flag2 == 0 || flag3 == 0);
printf("\n");
printf("New contact successfully added!\n");
printf("Press Enter to continue...");
getchar();
getchar();
return 0;
}
Oh by the way, the problem might simply be that the scanf call leaves the newline in the buffer, and if you loop and try again the first character seen will be the newline and scanf should not read any thing.
There are two things you should do: First check what scanf returns, it should return 1 if it read a string. Secondly you should tell scanf to discard any possible leading white-space by adding a space first in the format string: " %[^\n]".
Most scanf formats automatically skips leading white-space, but not when using the "%[" or "%c" formats.
Also, to not worry about writing out of bounds of the array, you should add a length modifier to make sure that scanf doesn't read more input than it can write: " %29[^\n]". If the length of the string is 29 after this, then you should probably read until you reach the end of the line, character by character.
Here is your program fixed:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
// In case you need this -- not needed for this case
void discard_input()
{
char c;
while( ( c = getchar() ) != '\n' && c != EOF );
}
void remove_trailing_newline(char * s)
{
char * ch = s + strlen( s ) - 1;
while( ch != s ) {
if ( *ch == '\n' ) {
*ch = 0;
break;
}
--ch;
}
return;
}
int main(){
char name[30];
char number[12];
int flag, flag1, flag2, flag3;
int i;
printf("Add New Contact\n");
do {
printf("\nInput name [1..30 char]: ");
fgets( name, 30, stdin );
remove_trailing_newline( name );
flag1 = flag = 1;
if ( !isalpha( name[ 0 ] ) ) {
flag = 0;
printf("First letter of name should be an alphabet (A-Z or a-z), found: %s\n", name );
}
// impossible
if (strlen(name) > 30) {
flag1 = 0;
printf("Length of name should be between 1 and 30 characters\n");
}
} while (flag == 0 || flag1 == 0);
do {
printf("\nInput phone number[6..12 digits]: ");
fgets( number, 12, stdin );
remove_trailing_newline( number );
flag2 = flag3 = 1;
int len_phone = strlen( number );
for (i = 0; i < strlen(number); i++) {
if ( !isdigit( number[ i ] ) ) {
flag2 = 0;
}
}
if (flag2 == 0) {
printf("Phone numbers should only contain digits (0-9), found:'%s'\n", number);
}
if ( len_phone < 6 || len_phone > 12) {
flag3 = 0;
printf("Length of phone numbers should be between 6 and 12 digits, found: %d\n", len_phone );
}
} while (flag2 == 0 || flag3 == 0);
printf("\n");
printf( "Name: '%s'\n", name );
printf( "Phone: '%s'\n", number );
printf("New contact successfully added!\n");
printf("Press Enter to continue...");
getchar();
return 0;
}
You can find the program here.
The fixings are more or less interesting, I enumerate they here:
At first, I thought that the problem was that the trailing new line was being left in the input buffer. fflush(stdin) is actually undefined behaviour in C, since the fflush() function is there for output streams. Anyway, I included the code in question 12.26b of the comp.lang.c FAQ, since I think it is interesing to have it as reference. Then, I decided to change scanf() with fgets(). This is due to the scanf() taking spaces as delimiters, so you wouldn't be able to write a complete name, i.e., name and surname. Remember that gets() is not an option, since it writes the input past the limit of the buffer. Actually, fgets() solves this by letting us define a limit of chars to read. The problem is that fgets() also includes the '\n' in the buffer, so, that's why I included the remove_trailing_newline() function. Tricky, isn't it?
You added a condition to check whether the name input had more than thirty chars. Actually, this is impossible to check in your program. First of all, fgets() will read 29 chars + the final char mark (0). Secondly, if you were actually allowing to input more than 30 chars, then the input would be written past the size of the buffer, which is undefined behaviour (crashes in most cases). You would have to use something more complex, like std::string in C++, and then check its length. Or maybe use a third party expandable string for C. Or roll out your own expandable string...
You can decide whether there is an alphabetic char or a digit by using isalpha(c) and isdigit(c) functions.
When you are going to use a value many times, such as strlen(name), then you should precompute it and store it in a local variable. Though a good compiler (its optimizer) will detect this situation and solve it for you, you never know which compiler is going to compile your code, and how advanced it is. Also, there is nothing wrong making things easier for the optimizer.
When you have a situation in which you set a flag for signaling an error condition, it is easier to set it to the "no error" value before checking anything, and solely in case of an error, set it to the "error" value. This will be easier to read, and therefore, to understand.
Hope this helps.

Validation using scanf() to protect against string

I have a code here that has a job to see if the user input is either string or integer from a range of 1-49. If I enter "asdas" it says invalid, and if I enter a integer from "1-49" it says valid. The problems I am having with this code is that if I enter "2 asda"
it will it count it has valid, and invalid at the same time, and if I enter "2 2" It will consider that valid as well. Just found out it also accepts "2d" as a valid input to.
for (i = 0; i < 6; i++)
{
printf("\nPlease enter the %d winning ticket numbers!: ", i+1);
if (scanf("%d", (&winningNumbers[i])) == 0 || (winningNumbers[i] <= 0) || (winningNumbers[i] >= 50))
{
inputFlush();
printf("\nInvalid Input. Please re-enter.\n") ;
i = i - 1;
}
}
for (i = 0; i < 6; i++)
{
printf("%d, ", winningNumbers[i]);
}
Read the entire line into a string (fgets, line 2 in the snippet). Read data from the string using sscanf: read the integer and one more character, after a space. Check whether sscanf returns something different than 1. If it does then you either have strings in the beginning (it returns 0 since it couldn't read an integer) or you have extra whitespace characters at the end (that is it also matched the %c format specifier). The space is needed to jump over whitespace until the end of the line (including the stored \n).
printf("\nPlease enter the %d winning ticket numbers!: ", i+1);
fgets(buffer, size, stdin);
if (sscanf(buffer, "%d %c", &winningNumbers[i], &c) != 1 || (winningNumbers[i] <= 0) || winningNumbers[i] >= 50))
{
// inputFlush(); not needed now that you read the entire line
printf("\nInvalid Input. Please re-enter.\n") ;
i = i - 1;
}
Look at this example.
#define MAX_LINE_SIZE 500
int main(int argc, char** argv)
{
char line[MAX_LINE_SIZE];
unsigned int num;
char *ptr;
while (fgets(line, MAX_LINE_SIZE, stdin) != NULL){
num = strtol(line, &ptr, 10);
if (line[0] != '\n' && (*ptr == '\n' || *ptr == '\0')) {
printf("Your num: %u\n", num); // check num if you need
} else {
printf("Error\n");
}
}
return 0;
}
The output:
12
Your num: 12
45
Your num: 45
34 2
Error
ads
Error
Here fgets function read data from stdin. strtol parse read string and assign an address of a char after parsed number to ptr pointer. Assuming that user has to input only one number (without any character after) we have to check whether *ptr is new line or end of line.
line[0] != '\n' prevents empty string.
Check the next character using peek, as detailed here. You can tell if it's good or not that way.
Another SO question that's pretty much the same.

Resources