Author : Tom Torfs
Page : << Previous 14 Next >>
the end of the loop.
printf("Enter your age [1-150]: ");
fflush(stdout);
Another prompt, with appropriate fflush(stdout).
if (fgets(buffer, sizeof buffer, stdin) != NULL
&& sscanf(buffer, "%d", &age) == 1
&& age>=1 && age<=150)
{
ok = 1;
}
else
{
ok = 0;
printf("Invalid age! Enter again...\n");
}
First another string is read from stdin using fgets() and the return value checked (this time we don't bother to remove the newline because we are going to convert this buffer to an integral variable anyway).
printf() has an opposite: the scanf() function. It also uses a format string like printf(), and is passed a variable number of arguments consisting of variables that correspond to a specific format specifier in the format string. But because the variables will be used to store an input result in, they must be prepended with the & address operator, so that their address is passed instead of their value (this is not required for arrays, such as strings, because they are always passed via their address anyway).
The format specifiers are mostly the same as those for printf() (see 3.3. printf). However, because an address is passed instead of a value there is no more float -> double, char -> int or short -> int promotion, so the following new format specifiers or modifiers are added/changed:
Format specifiers:
%i unlike %d, allows for entering hex (0x...) or octal constants (0...)
%n number of characters read upto now will be stored in signed int
%c exact number of characters specified by width specifier will be read
(when no width specifier is present, 1 character will be read)
%[character list] longest string consisting of the characters in the
character list will be read
%[^character list] longest string consisting of characters other than
those in the character list will be read
Modifiers:
h for d,u,x,o: short int instead of int
for n: result will be stored in short int
l for d,u,x,o: long int instead of int
for n: result will be stored in long int
for f,e,g: double instead of float
(note that this is different from printf(), where %lf etc.
may not be used for printing a double)
* assignment suppression
But the scanf() function itself is not very reliable. If the user enters wrong data, it can leave things on the input stream etc. which may be very annoying.
A better approach is to read a string using the fgets() function, and then process the input from this string using the sscanf() function. The sscanf() function works like the scanf() function, except that it takes as its first parameter the string where it has to read its input from. So in our case:
sscanf(buffer, "%d", &age)
Reads a decimal integer (%d) from the string buffer and stores it in the integer variable age (notice the address operator!).
However, we have to check whether all of this was successful because the user might also have entered something like "foo bar" instead of a valid age. Hence the code:
sscanf(buffer, "%d", &age) == 1
sscanf(), like scanf(), returns the number of variables it has been able to assign to properly. In our case this should be 1 if all went well.
&& age>=1 && age<=150
Furthermore we check that the entered age is between 1 and 150, otherwise we reject it as well.
We use a special feature of the logical operators && (and ||) here: they are the only operators that are guaranteed to execute from left to right, so we can be sure that by the time the sscanf() is reached the fgets() has been executed and by the time we check the value of age, the sscanf() has been executed. You may not rely on this with other operators (except the comma operator and the ?: operator). The && and || operators also have something called short-circuiting: if the left operand of the && operator is false, the right operand is guaranteed not to be evaluated. Similarly, if the left operand of the || operator is true, the right operand is guaranteed not to be evaluated.
if (fgets(buffer, sizeof buffer, stdin) != NULL
&& sscanf(buffer, "%d", &age) == 1
&& age>=1 && age<=150)
{
ok = 1;
}
else
{
ok = 0;
printf("Invalid age! Enter again...\n");
}
So if all these conditions are met, we set ok to 1 which will cause the loop to be ended. If they are not, we set ok to 0 which will cause the loop to be executed again, and we write an error message to inform the user that he has to re-input.
printf("Your age now is %d. In 10 years your age will be %d.\n",
age, age+10);
To make use of the fact that we now have the user's age in integer format, we do a little calculation with it.
Another function that is often used to read input from stdin is getchar(). It reads a single character, but does require the user to press enter. To avoid this, compiler/operating system specific functions have to be used. You have to be careful when using it similar to this:
if ((c=getchar()) != EOF)
/* code */ ;
In the above code, the variable c must be of type int, not char! The getchar() function returns an int, which may be an unsigned character value if a character was successfully read, or EOF (a negative value) if an error occurred.
12. Dynamic memory allocation
12.1. The persons program
/* prog12-1.c: persons */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct person {char name[30]; /* full name */
unsigned birthday, /* 1..31 */
birthmonth, /* 1..12 */
birthyear; /* 4 digits */
};
int main(void)
{
struct person *personlist;
unsigned number_of_persons, num;
char buffer[30];
char *p;
int year, month, day;
int ok;
do
{
printf("Enter the number of persons: ");
fflush(stdout);
if (fgets(buffer, sizeof buffer, stdin) != NULL
&& sscanf(buffer, "%u", &number_of_persons) == 1)
{
ok = 1;
if (number_of_persons>0)
{
personlist = malloc(number_of_persons * sizeof(struct person));
if (personlist==NULL)
{
printf("Not enough memory to store that many persons!\n");
ok = 0;
}
}
Page : << Previous 14 Next >>