i have tried to use k = getchar() but it doesn\'t work too;
here is my code
#include
int main()
{
float height;
float k=0;
do
Like Govind Parmar already suggested, it is better/easier to use fgets()
to read a full line of input, rather than use scanf()
et al. for human-interactive input.
The underlying reason is that the interactive standard input is line-buffered by default (and changing that is nontrivial). So, when the user starts typing their input, it is not immediately provided to your program; only when the user presses Enter.
If we do read each line of input using fgets()
, we can then scan and convert it using sscanf(), which works much like scanf()
/fscanf()
do, except that sscanf()
works on string input, rather than an input stream.
Here is a practical example:
#include <stdlib.h>
#include <stdio.h>
#define MAX_LINE_LEN 100
int main(void)
{
char buffer[MAX_LINE_LEN + 1];
char *line, dummy;
double value;
while (1) {
printf("Please type a number, or Q to exit:\n");
fflush(stdout);
line = fgets(buffer, sizeof buffer, stdin);
if (!line) {
printf("No more input; exiting.\n");
break;
}
if (sscanf(line, " %lf %c", &value, &dummy) == 1) {
printf("You typed %.6f\n", value);
continue;
}
if (line[0] == 'q' || line[0] == 'Q') {
printf("Thank you; now quitting.\n");
break;
}
printf("Sorry, I couldn't parse that.\n");
}
return EXIT_SUCCESS;
}
The fflush(stdout);
is not necessary, but it does no harm either. It basically ensures that everything we have printf()
'd or written to stdout
, will be flushed to the file or device; in this case, that it will be displayed in the terminal. (It is not necessary here, because standard output is also line buffered by default, so the \n
in the printf pattern, printing a newline, also causes the flush.
I do like to sprinkle those fflush()
calls, wherever I need to remember that at this point, it is important for all output to be actually flushed to output, and not cached by the C library. In this case, we definitely want the prompt to be visible to the user before we start waiting for their input!
(But, again, because that printf("...\n");
before it ends with a newline, \n
, and we haven't changed the standard output buffering, the fflush(stdout);
is not needed there.)
The line = fgets(buffer, sizeof buffer, stdin);
line contains several important details:
We defined the macro MAX_LINE_LEN
earlier on, because fgets()
can only read a line as long as the buffer it is given, and will return the rest of that line in following calls.
(You can check if the line read ended with a newline: if it does not, then either it was the final line in an input file that does not have a newline at the end of the last line, or the line was longer than the buffer you have, so you only received the initial part, with the rest of the line still waiting for you in the buffer.)
The +1
in char buffer[MAX_LINE_LEN + 1];
is because strings in C are terminated by a nul char, '\0'
, at end. So, if we have a buffer of 19 characters, it can hold a string with at most 18 characters.
Note that NUL, or nul with one ell, is the name of the ASCII character with code 0, '\0'
, and is the end-of-string marker character.
NULL (or sometimes nil), however, is a pointer to the zero address, and in C99 and later is the same as (void *)0
. It is the sentinel and error value we use, when we want to set a pointer to a recognizable error/unused/nothing value, instead of pointing to actual data.
sizeof buffer
is the number of chars, total (including the end-of-string nul char), used by the variable buffer
.
In this case, we could have used MAX_LINE_LEN + 1
instead (the second parameter to fgets()
being the number of characters in the buffer given to it, including the reservation for the end-of-string char).
The reason I used sizeof buffer
here, is because it is so useful. (Do remember that if buffer
was a pointer and not an array, it would evaluate to the size of a pointer; not the amount of data available where that pointer points to. If you use pointers, you will need to track the amount of memory available there yourself, usually in a separate variable. That is just how C works.)
And also because it is important that sizeof
is not a function, but an operator: it does not evaluate its argument, it only considers the size (of the type) of the argument. This means that if you do something silly like sizeof (i++)
, you'll find that i
is not incremented, and that it yields the exact same value as sizeof i
. Again, this is because sizeof
is an operator, not a function, and it just returns the size of its argument.
fgets()
returns a pointer to the line it stored in the buffer, or NULL
if an error occurred.
This is also why I named the pointer line
, and the storage array buffer
. They describe my intent as a programmer. (That is very important when writing comments, by the way: do not describe what the code does, because we can read the code; but do describe your intent as to what the code should do, because only the programmer knows that, but it is important to know that intent if one tries to understand, modify, or fix the code.)
The scanf() family of functions returns the number of successful conversions. To detect input where the proper numeric value was followed by garbage, say 1.0 x
, I asked sscanf()
to ignore any whitespace after the number (whitespace means tabs, spaces, and newlines; '\t'
, '\n'
, '\v'
, '\f'
, '\r'
, and ' '
for the default C locale using ASCII character set), and try to convert a single additional character, dummy
.
Now, if the line does contain anything besides whitespace after the number, sscanf()
will store the first character of that anything in dummy
, and return 2. However, because I only want lines that only contain the number and no dummy characters, I expect a return value of 1.
To detect the q
or Q
(but only as the first character on the line), we simply examine the first character in line
, line[0]
.
If we included <string.h>
, we could use e.g. if (strchr(line, 'q') || strchr(line, 'Q'))
to see if there is a q
or Q
anywhere in the line supplied. The strchr(string, char) returns a pointer to the first occurrence of char in string, or NULL if none; and all pointers but NULL are considered logically true. (That is, we could equivalently write if (strchr(line, 'q') != NULL || strchr(line, 'Q') != NULL)
.)
Another function we could use declared in <string.h>
is strstr()
. It works like strchr()
, but the second parameter is a string. For example, (strstr(line, "exit"))
is only true if line
has exit
in it somewhere. (It could be brexit
or exitology
, though; it is just a simple substring search.)
In a loop, continue
skips the rest of the loop body, and starts the next iteration of the loop body from the beginning.
In a loop, break
skips the rest of the loop body, and continues execution after the loop.
EXIT_SUCCESS
and EXIT_FAILURE
are the standard exit status codes <stdlib.h>
defines. Most prefer using 0
for EXIT_SUCCESS (because that is what it is in most operating systems), but I think spelling the success/failure out like that makes it easier to read the code.
I wouldn't use scanf
-family functions for reading from stdin
in general.
fgets
is better since it takes input as a string whose length you specify, avoiding buffer overflows, which you can later parse into the desired type (if any). For the case of float
values, strtof
works.
However, if the specification for your deliverable or homework assignment requires the use of scanf
with %f
as the format specifier, what you can do is check its return value, which will contain a count of the number of format specifiers in the format string that were successfully scanned:
§ 7.21.6.2:
The [scanf] function returns the value of the macro EOF if an input failure occurs before the first conversion (if any) has completed. Otherwise, the function returns the number of input items assigned, which can be fewer than provided for, or even zero, in the event of an early matching failure.
From there, you can diagnose whether the input is valid or not. Also, when scanf
fails, stdin
is not cleared and subsequent calls to scanf
(i.e. in a loop) will continue to see whatever is in there. This question has some information about dealing with that.