If statement inside while loop with the same condition - c

Is there a better way to write the following code by eliminating the repeated condition in the if statement in C?
while (n < 0) {
printf("Enter a positive integer: ");
scanf("%d", &n);
if (n < 0) {
printf("Error: please enter a positive integer\n");
}
}
Thank you.

Simply rework your loop breaking when correct input is given. This way the check is done only one time:
while (1)
{
printf("Enter a positive integer: ");
scanf("%d", &n);
if (n >= 0)
break;
printf("Error: please enter a positive integer\n");
}
And, as specified in comments, an optimized compiler should be able to reverse the loop by itself.

This is something that IMO is best accomplished with a bit of refactoring:
#include <stdio.h>
#include <stdbool.h>
static bool get_postive_integer(int *pOut) {
int n;
printf("Enter a positive integer: ");
scanf("%d", &n);
if(n < 0)
return false;
*pOut = n;
return true;
}
int main(void)
{
int n;
while (!get_postive_integer(&n)) {
printf("Error: please enter a positive integer\n");
}
}
Give the operation a name, check that it fails, and only then print a message accordingly. The success or failure condition is only coded once here, in the named operation.

You could use:
while (printf("Enter a positive integer: ") > 0 &&
scanf("%d", &n) == 1 &&
n < 0)
{
printf("Error: please enter a positive integer\n");
}
This stops if the printf() fails, if the scanf() fails, or if the value in n is non-negative. It's a good idea to always check that the scanf() succeeds. It is merely convenient that printf() returns the number of characters it wrote (or a negative number on failure) so it can be used in a condition too. You could add fflush(stdout) == 0 && into the stack of operations too.
Or you could decide that the code in the condition should be in a function:
static int read_positive_integer(void)
{
int value;
if (printf("Enter a positive integer: ") > 0 &&
fflush(stdout) == 0 &&
scanf("%d", &value) == 1 &&
value >= 0)
return value;
return -1;
}
and then the calling code is:
while ((n = read_positive_integer()) < 0)
printf("Error: please enter a positive integer\n");
There are many variations on the theme; you might wrap the while loop into a function; you might make the prompts into parameters to the function. You might decide to be more careful about reporting what goes wrong (different actions if printf() fails compared with what happens if scanf() returns 0 (non-numeric data in the input) or EOF (no more data in the input).

The following examples are presented in the spirit that people should know what is available in the language.1 The way I would usually write the code is shown in Frankie_C’s answer. As some have noted, optimization usually makes this simple case not worth worrying about, but the question is not limited to a simple evaluation like n < 0; the test might be a function call to some expensive evaluation of more complicated criteria.
Folks are not going to like this, but:
goto middle;
do
{
printf("Error, please enter a positive integer\n");
middle:
printf("Enter a positive integer: ");
scanf("%d", &n);
} while (n < 0);
If you are vehemently opposed to goto, you can use a stripped-down version of Duff’s device:
switch (0)
do
{
printf("Error, please enter a positive integer\n");
case 0:
printf("Enter a positive integer: ");
scanf("%d", &n);
} while (n < 0);
But you should not.
Footnote
1 Commonly, software engineers will have to work with code written by others, so they must be prepared to recognize and understand anything expressible in the language, even if that is just a first step toward rewriting it into better code. And occasionally situations arise where “ugly” code becomes desirable for commercial or other practical reasons.

Another alternative is to split into a function:
int func(){
int n;
printf("Enter a positive integer: ");
scanf("%d", &n);
return scanf("%d", &n) == 1 ? n : -1;
}
and the loop becomes
while ((n = func()) < 0){
printf("Error: please enter a positive integer\n");
}
although the assignment in the condition check is not to everyone's taste. Note that I return -1 if the return value of scanf is not 1, something that you should always check.
What I do though in this situation (See Eric's answer) is to write
switch (0) do {
printf("Error, please enter a positive integer\n");
case 0:
printf("Enter a positive integer: ");
scanf("%d", &n);
} while (n/*ToDo - make sure n is initialised if scanf fails*/ < 0);

Related

Goto and Code Repetition - Are they avoidable in this case?

I've recently faced a programming problem, and it seems to me that the most optimized way of solving it is by using goto, even though it's not a good practice. The problem is: tell the user to enter a positive natural number ( > 0) and read the input. If this number is valid, tell the user the square of that number. Do this while the input is correct. I came up with a few solutions, but all of them seem to have problems. Here are two of them:
Solution 1 - Problem: Uses goto
#include <stdio.h>
int main()
{
int num;
_LOOP:
printf("Enter a positive natural number: ");
scanf("%i", &num);
if (num > 0) {
printf("Square: %i\n", num * num);
goto _LOOP;
}
printf("Invalid number\n");
return 0;
}
Solution 2 - Problem: Double-checks if num > 0 (code repetition)
#include <stdio.h>
int main()
{
int num;
do {
printf("Enter a positive natural number: ");
scanf("%i", &num);
if (num > 0)
printf("Square: %i\n", num * num);
} while (num > 0);
printf("Invalid number\n");
return 0;
}
Obviously, there are more ways to solve the problem, but all the other ones I came up with that do not use goto encouter the same code repetition problem. So, is there a solution where both goto and code repetitions are avoided? If not, which one should I go for?
Here's half the answer; try to fill in what's missing. Remember that sometimes it's better to structure your loop as "do something until..." rather than "do something while..."
for (;;) {
printf("Enter a positive natural number: ");
scanf("%i", &num);
if (num <= 0)
break;
printf("Square: %i\n", num * num);
}
printf("Invalid number\n");
[updated with #rdbo's answer]
What about breaking out of the loop? This is basically a goto statement to the end of the loop without explicitly using goto.
#include <stdio.h>
int main()
{
int num;
while(1) {
printf("Enter a positive natural number: ");
scanf("%i", &num);
if (num > 0) {
printf("Square: %i\n", num * num);
} else {
printf("Invalid number\n");
break;
}
}
return 0;
}
The other option: check a bool that stores if the continue condition is met. It reads much easier than the infinite loop/break approaches (to me) and there's no code repetition.
#include <stdio.h>
#include <stdbool.h>
int main()
{
int num;
bool bContinue;
do {
printf("Enter a positive natural number: ");
scanf("%i", &num);
if (num > 0){
printf("Square: %i\n", num * num);
bContinue = true;
}
else{
printf("Invalid number\n");
bContinue = false;
}
} while (bContinue);
return 0;
}
For starters if you expect a non-negative number then the variable num should have an unsigned integer type for example unsigned int.
As it is written in your question the user can enter an invalid data or interrupt the input. You have to process such a situation.
Also the multiplication num * num can result in an overflow.
And using goto instead of a loop is indeed a bad idea.
Pay attention to that you should declare variables in minimum scopes where they are used.
To use the for loop for such a task is also a bad idea. It is much expressive to use a while loop.
The program can look the following way
#include <stdio.h>
#include <stdbool.h>
int main(void)
{
while ( true )
{
printf( "Enter a positive natural number: " );
unsigned int num;
if ( scanf( "%u", &num ) != 1 || num == 0 ) break;
printf( "Square: %llu\n", ( unsigned long long )num * num );
}
puts( "Invalid number" );
return 0;
}
The program output might look like
Enter a positive natural number: 100000000
Square: 10000000000000000
Enter a positive natural number: 0
Invalid number
Or it would be even better to move the last output statement inside the while statement. Fo rexample
#include <stdio.h>
#include <stdbool.h>
int main(void)
{
while ( true )
{
printf( "Enter a positive natural number: " );
unsigned int num;
if ( scanf( "%u", &num ) != 1 || num == 0 )
{
puts( "Invalid number" );
break;
}
printf( "Square: %llu\n", ( unsigned long long )num * num );
}
return 0;
}
I am a bit surprised nobody suggested functional decomposition yet.
Instead of writing a big pile of primitive statements, cut main up into smaller functions.
Apart from readability/maintainability benefits, it also helps eliminate code duplication in a very natural way.
In OP's case, getting input from the end user is a separate responsibility, and makes a good choice for a separate function.
static bool user_enters_number(int *ptr_to_num)
{
printf("Enter a positive natural number: ");
return scanf("%i", ptr_to_num) == 1;
}
Notice user_enters_number explicitly tests the return value of scanf.
This improve end-of-file handling.
Likewise, you could give number validation its own function.
This may seem like overkill (it's just num > 0, right?), but it gives us the opportunity to combine the validation with the resulting error message.
Printing "Invalid number" at the end of main feels wrong. Invalid numbers are not the only exit condition; end-of-file is another.
So instead, I will let the validation function determine the message.
As a bonus, this makes it possible to support multiple error types (e.g. separate messages for negative numbers and for zero).
static bool is_valid_number(int num)
{
bool ok = (num > 0);
if (!ok) printf("Invalid number\n");
return ok;
}
We now have two boolean-typed functions that can be neatly chained together with && and put inside the condition part of a loop, which is an idiomatic way of saying: if either one of these functions fails (i.e. returns false), immediately exit the loop.
What's left, is a very clean main function.
int main(void)
{
int num;
while (user_enters_number(&num) && is_valid_number(num))
{
printf("Square: %i\n", num * num);
}
}
To appreciate the benefits in terms of maintainability, try rewriting this code so that it accepts two numbers and prints their product.
int main(void)
{
int num1, num2;
while (user_enters_number(&num1) && is_valid_number(num1) &&
user_enters_number(&num2) && is_valid_number(num2))
{
printf("Product: %i\n", num1 * num2);
}
}
The changes are trivial, and limited to a single function
(though you might consider adding a parameter input_prompt to user_enters_number).
There is no performance penalty for this 'divide-and-conquer' approach: a smart compiler will do whatever is necessary to optimize the code, e.g. inline the functions.

Verifying inputs and repeating statements

I need to take two numbers from two separate scans and find the GCD of both of them. I have this currently which will take the two numbers, but I need to validate input with a function. It should work as follows, I enter a number and it checks to verify that it is a positive integer. If not it should prompt me to say that the input is unrecognized and ask for it again. Once I input a valid number, it should continue to ask me to put in a second one and do the same verification process. Any help with input validation and repeating the question in a function would be helpful.
int main()
{
int num1, num2, i, GCD;
printf("Please enter a positive integer: \n");
scanf("%d" ,&num1);
printf("Please enter a positive integer:\n");
scanf("%d", &num2);
if (num1 >0 && num2 > 0) {
for(i = 1; i <= num1 && i <= num2; i++)
{
if(num1 % i == 0 && num2 % i == 0)
GCD = i;
}
}
else {
printf("I'm sorry that number is unrecognized or negative.\n");
}
printf("The largest integer that divides both %d and %d is: %d\n",num1,num2, GCD);
return 0;
}
you can use a do-while loop and put your scanf in there as long as you dont get a valid answer. For example:
do {
// put your scans here
} while(num1<0 || num2<0);
Also you can catch the case if he inputs a character instead of integer input! So that is a good way to repeat a prompt until you get the valid answer.

Even numbers from an array (C)

I'm new to C and I can't figured it out why this code does not work properly.
The whole idea was to enter few numbers and output amount of even numbers.
int n, p;
printf("How many numbers would you like to enter?\n");
scanf("%d", &n);
int array[n];
printf("Enter your numbers\n");
for (int i = 0; i == n; i++) {
scanf("%d\n", &array[i]);
if (array[i] % 2 == 0) {
p++;
}
}
printf("Amount of even numbers: %d", p);
There are multiple problems in your code fragment:
p is uninitialized: the behavior is undefined. You must initialize p as int p = 0;
scanf("%d\n", &array[i]); will consume any trailing white space, forcing the user to type the next value before returning. Just use scanf("%d", &array[i]);
the loop test is incorrect: i == n is false at the first iteration unless n is 0. You should write:
for (int i = 0; i < n; i++) {...
Note that you do not need to store the input values into an array but you should check the return value of scanf() to avoid undefined behaviour on invalid input:
#include <stdio.h>
int main() {
int n, val, p = 0;
printf("How many numbers would you like to enter? ");
if (scanf("%d", &n) != 1) {
printf("invalid input\n");
return 1;
}
printf("Enter your numbers: ");
for (int i = 0; i < n; i++) {
if (scanf("%d", &val) != 1) {
printf("invalid input\n");
break;
}
if (val % 2 == 0) {
p++;
}
}
printf("Amount of even numbers: %d\n", p);
return 0;
}
There are multiple issues:
You haven't given an initial value for the variable p. Many programming language may give it a default value of 0 by default, by that's not the case with C/C++. In C/C++, the default value for a variable is simply a garbage value. What you're doing is incrementing a garbage value instead of incrementing a zero.
Your loop condition is wrong. You should loop while i < n, not i == n.
If you fixed these 2 points, your code will work. But wait! that doesn't mean the code is good. There are still potential issues:
NEVER create an array of unknown size on the stack. You don't know how large number the user may enter. Your program will crash, which is bad.
You're not using the array anyways. You can just take the number and increment your counter.
C is an unsafe language. Unlike many other languages, it does not do anything for you unless you tell it to. I the line:
int n,p
You create two variables, this tell the compiler to create space for them on the stack. What is does not tell the compiler is what to put into those variables. What ends up happening is whatever was in those memory locations at the time end up in the variables. This can be literally anything and in C terminology is undefined behaviour. (In C undefined is the same as very bad).
What you need to do to fix things is to use:
int n = 0;
int p = 0;
You can save the work of initializing n since scanf does it for you but until you are sure it is a good idea, always initialize variables.
As an aside, be careful with scanf you case is fine but when dealing with strings, scanf might overflow your variable. To be safe use sscanf. This allows you to specify your size.

Algorithm that asks for numbers until zero is input

I'm trying to make an algorithm in C that asks you to input any number, and stops asking when you input the number 0. I'm supposed to do it with a while loop, but it doesn't work and I tried everything I've learned. This is my code that doesn't work:
#include<stdio.h>
int main()
{
int number;
while(number != 0)
{
printf("Introduce a number: ");
scanf("%i",&number);
}
return 0;
}
Hopefully it's not too late to bring my two cents to the party.
The solution which others suggest is definitely possible and working solution, however, I think it can be done in a slightly neater way. For cases like this, do while statement exists:
#include <stdio.h>
int main() {
int number; // Doesn't need to be initialized in this case
do {
printf("Introduce a number: ");
if (scanf("%i", &number) != 1) { // If the value couldn't be read, end the loop
number = 0;
}
} while (number != 0);
return 0;
}
The reason I think this solution is better is just that it doesn't bring any other magic constants to the code, hence it should be better readable.
If someone saw int number = 42;, for example, he'd be asking - Why 42? Why is the initial value 42? Is this value used somewhere? The answer is: No, it is not, thus it's not necessary to have it there.
You need to assign a number to number before using it in the condition.
You have two options: a) use a dummy initial value, or b) scanf before test
// a) dummy value
int number = 42;
while (number != 0) { /* ... */ }
or
// b) scanf before test
int number; // uninitialized
do {
if(scanf("%i", &number) != 1) exit(EXIT_FAILURE);
} while (number != 0);
int number = 1;
while(number != 0){
printf("Introduce a number: ");
scanf("%i",&number);
}
Scanf will pause loop and waiting for typing a number

Programming in C. Error Handling an integer value

I have an integer input for my I.D. My teacher says I need to error handle all my inputs and basically display appropriate error messages if input entered is invalid. So I'm trying to get an error message to pop up if the user enters a real number or number less than one. But what I've tried is not working!
printf("Enter client Id--->");
scanf("%d", &client.id);
while ((client.id < 1) || (client.id % 1 != 0)) {
printf("Invalid I.d entered\n");
printf("Enter client Id--->");
scanf("%d", &client.id);
} // endwhile`
scanf("%d", &client.id);
You have to test the return value of scanf. If the result is 1, it has successfully scanned your integer. All you have to do next is to test that the value is >=1.
So, something like that:
while( 1 ) {
printf("Enter client Id--->");
int scanned = scanf("%d", &client.id);
if( scanned != 1 || client.id < 1 ) {
// note: infinite loop for invalid input, see note below
printf("Invalid I.d entered\n");
}
else {
break; // OK
}
}
NB(1) see there for a an explanation of why scanf is unsafe and ways of making it safe(r).
NB(2) you haven't included the client.id declaration, but it has to be an int type. Even if you wanted to scan a float instead of an int, x % 1 != 0 is a no-no.
You can add a printf after the scanf to ensure scanf is working as expected.
Still, regardless of what value is entered, the program exits without entering while() body.
Of the two while() conditions, the first is innocuous, but the second is suspect because it is false for all values.
To check that a number is not an integer, the easiest way (the way without function calls) would be client.id != (int)client.id.
But first, you must read the number as a floating-point type, precisely so you can catch that error. My code:
double x;
printf("Enter client Id--->");
scanf("%lf", &x);
while((x < 1.0) || (x != (int)x))
{
printf("Invalid I.d entered\n");
printf("Enter client Id--->");
scanf("%lf",&x);
} // endwhile
client.id = (int)x;

Resources