Author : Tom Torfs
Page : << Previous 16 Next >>
{
ok = 1;
}
else
{
ok = 0;
printf("Invalid number! Enter again...\n");
}
This is also analogous to our previous examples. Again, an explicit fflush(stdout); is needed because we don't end our prompt with a newline. The only difference in the sscanf() call is that the %u format specifier is used instead of the %d format specifier because number_of_persons is an unsigned integer instead of a signed integer.
However, after the ok=1; statement something new follows:
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;
}
}
If the number of persons entered is greater than 0, memory is allocated for our personlist pointer [*]. The malloc() function is used for that.
[*] We explicitly check for the 0 case, because according to the standard, the behaviour of malloc(0) is implementation-defined.
The parameter of malloc() is the number of bytes that should be allocated. In our case we use:
malloc(number_of_persons * sizeof(struct person))
The sizeof operator (as seen in 4.2. Operators and typecasts) will give the size of its operand in bytes (the form with parentheses is used for types, the parentheses are not required for variables). Therefore sizeof(struct person) will give the number of bytes required to store 1 person structure [*]. By multiplying this value by the entered number of persons, we get the total number of bytes required to store all person structures.
[*] Another, slightly better method would be to use sizeof *personlist instead of sizeof(struct person), because this will remain correct even if we change the type that personlist points to to something other than struct person.
The malloc() function returns a generic pointer to void which points to the allocated memory. By assigning this value to the personlist pointer, our pointer now points to actual usable memory:
personlist = malloc(number_of_persons * sizeof(struct person));
However, it is quite possible for the malloc() function to fail, usually because there is not enough memory available. In that case the returned pointer will be the NULL pointer (invalid pointer value). So we must explicitly check for this case after every call to malloc():
if (personlist==NULL)
{
printf("Not enough memory to store that many persons!\n");
ok = 0;
}
In our case, if the malloc() fails, we just reset our flag variable to 0 (not ok) and the user will be asked to enter a new value.
if (number_of_persons==0)
{
printf("OK, perhaps another time!\n");
return 0;
}
If the entered number of persons was 0, no memory has been allocated in our above code, and we return to the operating system.
for (num=0; num<number_of_persons; num++)
{
}
We loop using the num variable as counter from 0 to number_of_persons-1. We don't count from 1 to number_of_persons because, as seen before, in C array indexes etc. start counting from 0, and it's best to maintain consistency throughout the program.
printf("\nEnter the information for person #%u:\n", num);
printf("Name: ");
fflush(stdout);
if (fgets(buffer, sizeof buffer, stdin) == NULL)
{
printf("Error reading from stdin; input aborted\n");
number_of_persons = num;
break;
}
p = strchr(buffer,'\n');
if (p!=NULL)
*p = '\0';
if (strlen(buffer)==0)
{
printf("Input stopped\n");
number_of_persons = num;
break;
}
strcpy(personlist[num].name, buffer);
Most of this should also look familiar. After the input has been entered into the buffer array and checked that no error has occurred, and the newline has been removed, we check whether buffer is an empty string (i.o.w. the user has pressed enter without entering any data). If that is the case, we stop our input, and adjust the number_of_persons variable accordingly (we don't need the original value of number_of_persons, namely the total number of persons for which memory has been allocated, further in this program; otherwise we would have to use a separate variable for the number of persons for which data has been entered) and exit the for() loop with a break; statement [*]. If the fgets() function returned NULL an analogous logic is followed.
[*]See 9.3. Using break,continue,goto. This is an example of where break; can be useful.
After we verified that the input is valid (i.o.w. not an empty string), we copy the string in buffer to the name member variable of the current structure in the personlist. The n-th element (in this case structure) in the memory that personlist points to is accessed like this: (here as well, n starts counting at 0)
personlist[n]
In other words, just like if personlist was an array of struct instead of a pointer to struct. This is a very handy similarity between arrays and pointers, however you must not let it confuse you into thinking that arrays and pointers are the same.
The member selector operator (.) is then used to access a member variable of this structure, just like if it were a simple struct variable, so that:
personlist[num].name
Gives us the name member variable of structure number num (which starts counting at 0) in the memory pointed to by personlist, which is used as the target string of our strcpy() function:
strcpy(personlist[num].name, buffer);
do
{
printf("Birthday [YYYY-MM-DD]: ");
fflush(stdout);
if (fgets(buffer, sizeof buffer, stdin) != NULL
&& sscanf(buffer, "%d-%d-%d", &year, &month, &day) == 3
&& year>=1000 && year<=9999
&& month>=1 && month<=12
&& day>=1 && day<=31)
{
ok = 1;
}
else
{
ok = 0;
printf("Invalid birthday! Enter again...\n");
}
}
while (!ok);
Another input loop which will keep repeating until the entered data is valid. The sscanf() function this time scans for 3 variables instead of 1 (that's also why we compare its return value against 3 instead of 1). In the if condition, which again makes use of the fact that the && operator always evaluates from left to right, after the input has been read without errors and properly processed by the sscanf() function, the
Page : << Previous 16 Next >>