Topic : An introduction to C
Author : Tom Torfs
Page : << Previous 8  Next >>
Go to page :


first going to see something else that's often useful: obtaining random numbers.
However, the oversize calculator that your computer really is cannot return really random numbers [*]. Instead, it uses something called pseudo-random numbers: they are numbers that are calculated using a mathematical algorithm, and which are therefore not really random at all, but which have the appearance of being random.
[*] Some specialized hardware may be used to measure random factors from e.g. the environment, but this falls way outside of the scope of the standard C language.
Obviously, if a program uses a certain mathematical algorithm to obtain these numbers, it will produce the very same numbers every time it runs. Not very random. That's why we must first initialize the random number generator with a value that is different nearly every time the program is run. A good value for that seems to be the current date and time.
The current date and time is returned when we call the time() function with NULL as parameter (see the section on time() in 16. Overview of the standard library if you want to know what this parameter is for). The value it returns is a time_t, and we don't really know what that is (it can be different on every system). All that interests us here is that the value will probably be different every time the program is run. [*]
[*] On some systems without a clock time() may return the same value (time_t)-1 every time, and on those this approach will of course fail.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


The standard header stdlib.h contains the declarations for the srand() and rand() functions, and the standard header time.h contains the declaration for the time() function.

   srand((unsigned)time(NULL));

The srand() function initializes the random number generator. It takes an unsigned integer value as parameter. As explained above, we use the return value of time(NULL) as the initializer (often called the seed of the random number generator), which is this unknown type time_t. We explicitly typecast (see 4.2. Operators and typecasts) this type to an unsigned integer [*] to suppress possible compiler complaints (since we know what we're doing and don't really care if for example we may lose some of the time information in the conversion; all that matters is that the value should be different every time the program is run, when possible).
[*] On some rare systems this may cause an overflow error.

   yesno = rand() % 2;

The rand() function returns a random number between 0 and RAND_MAX (the value of RAND_MAX is defined in stdlib.h and may be different on every system). You can't force rand() to return random numbers in a certain range. Instead, we convert its return value so that it fits in this range. The most obvious method to do this is the one we use above: using the remainder operator. For example, rand() % 10 will always give a value in the range 0..9. [*] So in our case yesno will contain either the value 0 or 1.
[*] This method is not very good, but used here for its simplicity. A better method (taken from the c.l.c FAQ) would be to use something like: (int)((double)rand() / ((double)RAND_MAX + 1) * N)
For more information, see question 13.16 in the c.l.c FAQ, the URL can be found in: 17.2. Other interesting C-related online material7.3. Using if/else

   if (yesno==0)
      printf("No\n");
   else
      printf("Yes\n");


In the preceding code, yesno was set randomly to either 0 or 1. This code now prints "No" when the value of yesno was 0, and "Yes" if it was any other value (which would be 1 in this case). The if() statement works as follows:

if (condition)
   statement_that_is_executed_if_the_condition_is_true;
else
   statement_that_is_executed_if_the_condition_is_false;


Note that, unlike in Pascal, there must be a semicolon after the code in the if part of the statement.
If the code in the if or else part of the statement consists of more than one statement, a block must be used:

if (condition)
{
   possibly_more_than_one_statement;
}
else
   still_one_statement;


When a block is used, no semicolon may be put after the closing }.
You can chain if statements as following:

if (condition)
   some_code;
else if (another_condition)
   some_other_code;
else
   even_different_code;


7.4. Using the ?: operator
The ?: operator may be used as an alternative to an if-statement, with the difference that the ?: operator yields a value. For example:

a = c + (b<4 ? 1 : 2);

Is equivalent to:

if (b<4)
   a = c + 1;
else
   a = c + 2;


So, the expression:

   condition ? expr1 : expr2

Will give expr1 if condition is true and expr2 if condition is false.
You have to be careful with predecence: the ?: operator has very low precedence (only assignment and comma operators have lower precedence). If you're not sure, it's safest to use enough parentheses.

8. switch/case/default
8.1. The animals program

/* prog8-1.c: animals */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

enum animal {CAT, DOG, COW, SNAIL, SPIDER, NUMBER_OF_ANIMALS};

void animalsound(enum animal animalnumber)
{
   switch(animalnumber)
   {
   case CAT:
      printf("Miaow!\n");
      break;
   case DOG:
      printf("Woof!\n");
      break;
   case COW:
      printf("Moo!\n");
      break;
   default:
      printf("Animal makes no audible sound.\n");
      break;
   }
}

void animalfood(enum animal animalnumber)
{
   switch(animalnumber)
   {
   case COW:
   case SNAIL:
      printf("Eats plants.\n");
      break;
   case CAT:
   case SPIDER:
      printf("Eats beasts.\n");
      break;
   }
}

int main(void)
{
   enum animal myanimal, youranimal;

   srand((unsigned)time(NULL));

   myanimal = rand() % NUMBER_OF_ANIMALS;

   animalsound(myanimal);
   animalfood(myanimal);

   youranimal = rand() % NUMBER_OF_ANIMALS;

   animalsound(youranimal);
   animalfood(youranimal);

   return 0;
}


This program's output varies. It might be something like this:

Moo!
Eats plants.
Animal makes no audible sound.
Eats beasts.

8.2. Using the switch() statement

enum animal {CAT, DOG, COW, SNAIL, SPIDER, NUMBER_OF_ANIMALS};

This is yet another familiar enum. Again, NUMBER_OF_ANIMALS will be set to the number of defined animals.

void animalsound(enum animal animalnumber)
{
}


This time we put part of our program in a separate function. This way the same code can be used several times without having to be completely written out every time (see also 2.4. Functions and types).
In this case, the animalsound() function will print out the corresponding sound for a given animal of our above enum type.

   switch(animalnumber)
   {
   case CAT:
      printf("Miaow!\n");
      break;
   case DOG:
      printf("Woof!\n");
      break;
   case COW:
      printf("Moo!\n");
      break;
   default:
      printf("Animal makes no audible sound.\n");
      break;
   }


This is the new part in our program. A switch statement executes certain code depending on the value of the variable that is switched upon (the variable between the switch() parentheses, in this case animalnumber). For every different possible case a "case value:" is present, followed by the code that is to be executed in case the switched variable is equal to the specified value.
Every case should end with a break; statement, because otherwise the code simply keeps continuing into the next case statement. This is known as fallthrough, and has both its advantages and disadvantages. Once you're used to it it shouldn't pose any problems, you just have to remember to put the break; in there. See below for a useful example of fallthrough.
A special case is the default: case. If a default: case is present, its code is executed when none of the other cases match. In our case the animals SNAIL and SPIDER don't have explicit case labels, and for them the default: case of "Animal makes no audible sound." will be executed.
The above code would be equivalent to: (apart from the fact that the animalnumber is evaluated multiple times in the if form)

if (animalnumber==CAT)
   printf("Miaow!\n");
else if (animalnumber==DOG)
   printf("Woof!\n");
else if (animalnumber==COW)
   printf("Moo!\n");
else
   printf("Animal makes no audible sound.\n");


But is obviously clearer and easier to expand.
Another difference between the if() and switch() form is that in the if() form non-integral types like double may be used. However, floating point variables may not be able to store an exact value, so you should not compare them with the equality operator (==). Instead you should limit the absolute value of the difference between the two to a certain percentage of the values, something like:

if (fabs(x-y) <

Page : << Previous 8  Next >>