So, I am new to this site and I am new to programming. I am at my first book and my dad gave a "homework", he doesn't know the codes to do what this, but I want to do it. So here it is what I want to do: I want to create a program like a calculator where you input a value A and a value B and then the program shows the result of A + B. I have done that, the problem is that if I input a character the program keeps reading the other codes. Here is what i have written, maybe you will understand better.
#include <stdio.h>
#include <conio.h>
#include <ctype.h>
void main(void)
{
int val_a;
int val_b;
int result;
printf("Write a number A=");
scanf("%d", &val_a);
if(isdigit(val_a))
{
printf("\nWrite a number B=");
scanf("%d", &val_b);
}
else
printf("\nI said number.\n");
result = val_a + val_b;
printf("%d + %d = %d", val_a, val_b, result);
getch();
printf("\033[2J"); \\ to clear the screen
}
So what I want is that the program will come back to where I have to input the number A. I found on google about labels, but I didn't understand anything :D. Maybe someone can post an example of what I want so I can study it.
Should i write my name? Maybe next time :P
PS: I hope someone will find a NOOB mistake in what I wrote and tell me about it. :pray:
You got the
isdigit
function all wrong. It is meant to receive a char argument but in your case the int gets converted, downgraded you might say.The function is based on ASCII codes of letters, link here. You can try this to see for yourself.
Furthermore, what you need is a
do{ /* something */ } while(boolean);
You need to check if
scanf()
successfully read the input it should read. In general, you should always check after reading that the read was successful.scanf()
returns the number of successfully read inputs which essentially map to the number of format specifiers it could successfully read. For example, you should check thatThe value read may or not be be a "digit". In general, it is probably not a digit according to
isdigit()
because this function only returns true for integer values of the characters'0'
,'1'
, ...,'9'
. Testing ifscanf()
could read all its format specifiers should avoid this problem.If
scanf()
fails you can read one character (using e.g.fgetc()
) and retry again or read an entire line (not usinggets()
but usingfgets()
). You might also want to print a message the input wasn't what was expected.Critique
So you are working on Windows.
The return type of
main()
should beint
, even though you'll find lots of examples to the contrary.You should be testing the return value from
scanf()
to see whether it was able to read a number or not.This test 'works', but it checks whether the number entered was a value in the range 48..57 (which are the ASCII, CP1252, Unicode, ISO 8859-1, etc codes for the digits
'0'
to'9'
). This probably wasn't what you had in mind.The user entered a newline, so the newline in front ot the
printf()
format string is not necessary, though it is also not harmful. The same comment about checkingscanf()
applies here, too.It is good to do error checking. However, you continue onwards even if you detect the error. You really need to stop.
On the whole, it is best to end lines with a newline. It flushes the data to the file or screen.
The
printf()
format string is not portable. Given that you included<conio.h>
, you should probably useclrscr()
(which probably does send that escape sequence, or an equivalent, to the terminal). I'm not convinced that it is necessary to clear the screen at the end of the program, but I work mainly on Unix systems, not with Windows.Given that MSVC is still a C89 compiler, you should include
return 0;
at the end of the program to return a success status.Rewritten
Adding all the changes, you'd end up with:
When you've learned about writing functions, you might compress that to:
The
get_a_number()
function is not a general purpose function, but it is useful in this context since it encapsulates a common piece of code.That's a quote from Kernighan & Plauger, "The Elements of Programming Style".
Using
atexit()
The code above was updated to include
getch()
andclrscr()
on all exit paths (and to remove#include <ctype.h>
since none of the functions from it were used any more). It's a nuisance to write out the two function calls repeatedly. Another way to avoid that problem is to write a function that is called when the program exits; you register it with theatexit()
function, which is declared in<stdlib.h>
:That code in
main()
assumes you can mix statements (such asatexit()
) and declarations (such asint val_a
) arbitrarily. That is permitted by both the current (2011) and old (1999) versions of Standard C, but was not permitted by the original (1989) version of Standard C. I believe MSVC adheres to the original standard. If so, you probably need to write thatmain()
as:It is a fine discussion point whether the code inside the inner block (the inner braces) should be indented an extra level or not. You're always allowed to declare variables at the start of a block of code enclosed in braces, so that allows me write the call to
atexit()
and then define and use those variables.If you read about Agile programming, you'll find that one of the mantras is:
The rewrites above to avoid the repeated
getscr()
calls could be considered an application of that mantra. Similarly with theget_a_number()
function; it to applies the DRY mantra.Trying again
If you want the program to go back and prompt again if the user makes a mistake in their typing, then you need to modify the
get_a_number()
function. And, because the function grows considerably more complex, you definitely need to be using a function for the job. You also run into a problem withscanf()
; it doesn't allow you to enforce one number per line. If you typed1 2
in response to the first prompt, the first call toget_a_number()
would read the one and the blank, stop parsing (and put the blank back for reuse), and return with the value 1. The second call toget_a_number()
would output the prompt but return with the value 2 without the user typing anything more. If that's not the behaviour you want, then you have to arrange to read the whole first line and scan for the number, and then discard the rest. There's another reason for worrying too. If the user typesa
instead of1
, thescanf()
will never read anything more; it will go into a loop, finding that thea
is not valid as a digit, returning 0 conversions complete, and your code will probably end up in an infinite loop, prompting for input and reading thea
. This sort of problem is why many people (particularly me) avoid usingscanf()
.The simplest way to avoid most of the problems above is with
fgets()
andsscanf()
:That covers quite a lot of issues. Often, it is a good idea to echo back the erroneous data. You could do that with:
This uses 'adjacent string concatenation', and shows the user what the program received, which can often help the user understand what's wrong.
I used
fputs(prompt, stdout)
instead ofprintf(prompt)
to output the prompt. Either could be used (printf(prompt) != 0
would be the test), but there is a slight advantage tofputs()
because if someone managed to subvert your program and get a%
into the prompt, theprintf()
would try to process an argument it was not passed, whereasfputs()
does not interpret its argument string at all. Here, the difference is negligible; it can reasonably be argued that I'm over-complicating things. But the security of programs is also something you end up learning, and techniques such as "do not useprintf(variable)
" can help make your programs more resilient. (Actually, the complication is the explanation; the code is quite simple.)Another detailed discussion point is 'should there be a
fflush()
afterfputs()
and beforefgets()
?' It would be possible to add the extra 'condition'fflush(stdout) == 0 &&
to the loop control, but in practice it is usually unnecessary. It would only make a difference if standard input was coming from something other than the terminal and standard output was going to a file. So, I left it out.The counter in the loop prevents things going badly awry — the program exits rather than hang around forever, frustrating the user who can't get the data entered correctly. Have you ever had a program pop up a dialog box at which you can only hit the 'OK' button (even though its not OK) and then come back with the same box again, over and over and over again? I hope not for your sake. I have; it frustrates me when it happens.
And you can see why it is advantageous to write the code in
get_a_number()
just once, rather than twice; that would be a lot of repetition.You misunderstand the use of the isdigit function. It determines if a character is a digit such as '0', '1', '2', etc. However, you've already converted the input to an integer with the scanf function. So simply delete the call to isdigit and instead check the return code of scanf, which will tell you if the user entered a valid number.