Lecture 5: Variables


Types of Variables

Variables store values and information. They allow programs to perform calculations and store the results for later use. Imagine a variable as a box that can contain information. When we want to know this information, we open the box and read the value. At the end, we put the information back in the box and leave it there, until we need the information again.

For ease of identification, to avoid confusion, every box must have a name, an identifier (see aula 4).

The type of the box defines the size of the box and the type of information that can be found in there. Variables come in many sizes and flavours.

Variables can store numbers, names, texts, etc. In modern versions of C there are many types of basic variables. The exact implementation of the types of variables depends a little on the compiler. For the Borland C compiler we have:
 

 group
 variable type
 range
 size it occupies in memory
II
 unsigned char 
0 .. 255
 8 bit = 1 byte
II
 char
 -128 .. 127
 8 bit = 1 byte
II
 int
 -32 768 .. 32 767
 16 bit = 2 bytes
II
 unsigned int
 0 .. 65 535
 16 bit = 2 bytes
II
 long
 -2 147 483 648 .. 2 147 483 647 
 32 bit = 4 bytes
II
 unsigned long
0 .. 4 294 967 295
 32 bit = 4 bytes
III
 float
 -3.4E-38 .. 3.4E38
 32 bit = 4 bytes
III
 double
 -1.7E-324 .. 1.7E308
 64 bit = 8 bytes
III
 long double
 -3.4E-4932 .. 1.1E4932
 80 bit = 10 bytes
IV
 char
 #0 .. #255 ASCII
 8 bit = 1 byte

Notes:


I Boolean is used to store and manipulate information of the type true-or-false or yes/no. As we have seen in lecture 3, this is one bit of information. In C this type of variable does not exist. Instead it is emulated with int's, where 'false' is equal to 0 and any other number is equalto 'true'.
 
 

II int, unsigned int, and long and char and unsigned char all store values of complete numbers. This is used for things that can be counted; number of people in the room, number of doors of a car, number of phonecalls somebody made, ano lectivo, day of the month, etc. Unsigned char and int only store positive numbers, while int and long can store both positive and negative numbers, at the cost of limited maximum value. The char occupies the least memory, only 8 bits, but the range of values it can take is therefore very limited, only 256 different values. If we need to store larger numbers, we have to use int. If we want to store even larger numbers and want positive and negative numbers we use long.
Note: since a char has 8 bits, it can store 28 = 256 different numbers: 0 .. 255 or -128..127. The same calculation we can make for the 16-bit units int and unsigned int: 216 = 65536, numbers from 0 .. 65535 for unsigned int, and -32768 .. 32767 for int. Remember the calculations of lecture 3.

III Float, double and long double are examples of variables that can store floating point numbers (for example 3.1415926535). These are used for things that are not countable, like the length of the car, the time elapsed between events, the height of a building, the square-root of 3, etc. The smallest is float, it occupies only 4 bytes, at the cost of smaller precision in our calculations. The best is long double, with 80 bits (10 bytes), the calculations will have very high precision, but the calculations will be slower and it occupies more space in memory. A good middle way is the double.

IV The last type is used for storing text. Char is used for a single character of ASCII code. Since char can be used to store information of the type integer numbers as well as ASCII characters, we have to be very careful to not make mistakes.

Later we will learn how we can define our own type of variables, now let's take a look at how we use them in C


Variables: Declaration!

In most modern compiled languages, all variables that we will use have to be defined first. This is called declaration. To be more precise, declaration means reserving space in memory and associating a name to it, so that later we can use the name instead of the memory address when we want to retrieve the information.
In C we declare variables by writing the name of the variable preceded by the type of the variable. For example
  int i;
The place to do that is in the beginning of the function (main), before the first real instructions For example:

main()
{
  int i;
  float f;
  long li;

  instruction1;
  instruction2;
}
 
Everytime when the program runs and an instruction like int i; is encountered, space is reserved in memory and the address of this space is remembered for later reference when we need to store information in this box or retrieve a value from it. This space is released at the end of the function (or program in case of main).
For the experts: Variables are placed on the stack and stay there until the scope of the variables has expired.

If we want to define more variables of the same type, we can do that on a single line with the variables separated by commas, although it is nor prohibited to use several lines to define different variables of the same type:

main()
{
  int i, j, k;
  float f;
  float g;
  int n;

  instruction1;
  instruction2;
}
Forgetting to declare variables will cause a compiler error.


Problems with variables I: Type mixing

Always be careful when using diferent types of values in calculations. Consider the following code

main()
{
  int i, j;
  float f;

  i = 1;
  j = 3;
  f = i/j;
  printf("%f", f);
}
What will be the value of f at the end? in other words, what will be the output of the program?
NOT 0.3333 as we might expect. Instead the value of f will be 0.0000. This is because the division calculation is done with int's and in integer calculation 1 divided by 3 is 0. This is then attributed to f.
To avoid this we can

  * force the type of the value in the calculations. Thisis called "type casting". For example:
     f = (float) i/ (float) j;

  * Where we are calculating with constants we can force the type by including the seemingly redundant floating point part:
     f = 1.0 / 3.0;
   instead of
     f = 1 / 3;


Problems with variables II: Overflow


Consider the following program

main()
{
  int i, j, k;

  i = 20000;
  j = 20000;
  k = i + j;
  printf("%d", k);
}
Again, what will be the output of the program?. Naively we might think 40000, but k is of the type int and int's only go up to 32767. What is done with the rest? What happens is that the overflowing part "reenters at the bottom of the range". In this case we will get -25536. An overflow can occur when we add two numbers with limited range. An underflow can occur when we substract 2 numbers.
This is easier shown with a byte-sized variable (a byte has 8 bits and a range from 0 to 255 and is called unsigned char in C):
 
i
130 =
 10000010
j
130 =
 10000010
i+j 
(260)=
  100000100

What happens is that the 9th bit in the above equation is ignored and the result will be binary 00000100 which is equal to 4 in the decimal system. Therefore, for byte calculation 130 + 130 = 4.

What can be done to prevent this


Problems with variables III: Initialization

Declaring a variable does not assign a value to the variable, it only reserves space for it in memory!
main()
{
  int day;

  printf("Today is day %d\n", day);
}
In the program above, the output might be

Today is day 23741

When a computer is switched on, the memory is normally filled with 0's, but after a while, after many programs have been using the memory and left there their garbage there, the contents of a memory address is unpredictable. To ensure that we are working with well defined values we always must assign a value to each variable. In the next lecture we will learn how we can do that with assignment instructions in the program. Here it suffices to say that: "don't assume that your variables are set to 0 in the beginning".
Note: In many programming languages it is possible to assign a value to a variable at the moment of declaration. To do that in C we can use
  int day = 0;


printf

The instruction printf is one of the most useful instructions in C and is used for for showing information on the screen: text, numbers, values of variables, etc. Nearly every program has somewhere a printf statement. We have already seen some example above and skipped the discussion about how this functions works until now.
printf is part of the standard-input-output library (stdio.h). The output is formatted, which means that we can specify how exactly the information should be shown (how much space it should take onthe screen, how many significant digits should be shown for floats, etc.). The format is always the first argument of the function. The next argument(s) are the information to be shown, separated by commas.
  printf("%d", i);
will show the value of the integer i
  3
for every piece of information we have to specify the format:
  printf("%d %d", i, j);
  3 5
other format specifiers that are interesting for us:
 
%d
%i
%u
%x
%f

%e
%c
%s

integer with sign
integer with sign
integer without sign
hexadecimal
float (31.2000)
for example %10.3f shows the float with 10 spaces on screen and 3 decimal cases
float with scientific notation (3.12E1)
ASCII character
string

Note that we can also directly write text in the format:
  printf("Hello%d World", i);
which will give as output (when i=3)
  Hello3 World
There are special characters that we can use to control the position of the output. There are written with an \. The most important is:
 
\n  goto to the beginning of the next line

For example
  printf("Hello\n%d World", i);
will give as output (when i=3)
  Hello
  3 World


Quick test:

To test your knowledge of what you have learned in this lesson, click here for an on-line test. Note that this NOT the form the final test takes!

Peter Stallinga. Universidade do Algarve, 22 October 2002