Lecture 15: ... of variables and functions



This lecture:

Scope of variables: Global or local


 Global variables are variables that can be used everywhere in the program

 Local variables are only defined inside the function where they were declared.
With this new information, it is much more clear which variable we can use when. Inside functions we can use the global and the local variables but functions cannot use eachothers variables. The function main cannot use the variable of the function module1, int y. Module1 cannot use the variable of function main, int z. Both functions can use the global variable x, however.
Warning: try to avoid the use of global variables in functions as much as possible. The reason why is simple. If we want to copy the function for another program this will be more difficult, becuase the new program probably will not have the same global variables. Using only local variables in functions is therefore much better. If you want to use the global variables, pass them as parameters to the functions. Ideally, a function is a stand-alone unit.

 
One more rule, typically for C and languages alike (single-pass compilers): variables can only be used in places AFTER their declaration in the program, so, if we put the declaration of a variable after a function, this function cannot use the variable, even if the variable is global.
Let's take a look at some examples. First a program of lecture 13:

#include <stdio.h>

 /* declare global variables x and y */
double x, y;

double square(double r)
{
 /* declare local variable localr */
  double localr;

 /* using the local variable localr */
  localr = r*r;
 /* using the global variable x */
  x = localr;
  return(x);
}

void main()
{
  x = 4.0;
  y = square(x);
}


Priority


Variable x is a local and a global variable.
Inside the function, the local variable
will be used.
When local and global variables exist with the same name, the local variable has higher priority and will therefore be used inside the function. Anyway, this is confusing, so always try to avoid using the same identifier again!

Some languages do not have the difference between local and global variables (for example BASIC). This will mean that we cannot use the same name for a variable twice.


Passing by value or by reference

When passing parameters to functions, we can do this in two different ways, either passing by value, or passing by reference.
Passing by value:
Until now we have only seen the first type. In this way, only a value is passed to the functions. Whatever we do with that value in the function will have no effect on the original value of the variable used in calling the function. As an example, to make this more clear. Assume we have a function that writes the square of the parameter p. To calculate the square we assign a new value (p*p) to p. The value of p will therefore change inside the function:
  void write_square(double p)
  {
    p = p*p;
    printf("%f", p);
  }
When we now call the function with a variable x, the value of this variable will not change by calling the function. In the main program:
  void main()
  {
    double x;

    x = 2.0;
    write_square(x);
    printf("%f", x);
  }
After returning from the function, the value of x hasn't changed. The total output of the program above will therefore be
  4.0
  2.0

Passing by reference:
On the other hand, if we do want to change the value of the variable used in calling the function, we can do this by passing the address of the variable to the function. All changes to the contents of this address are therefore permanent
  void write_square(double *p)
   /* p is a pointer-to-double */
  {
      /* change the contents of address p: */
    *p = *p * *p;
      /* show the contents of address p: */
    printf("%f", *p);
  }

  void main()
  {
    double x;

    x = 2.0;
    /* now we have to pass the address (&) of the variable x: */
    write_square(&x);
    printf("%f", x);
  }
If we now run the program, the output will be
  4.0
  4.0
because the value of x has changed simultaneously with the the contents of address p.
 

 
Passing by value
Passing by reference
In the analogon of boxes visualizing variables: passing by reference is handing over the box (variable) to the funtion which can then use and change the value in the box, while passing by value is equivalent to opening the box, copying the value and handing only that value over to the function. Obviously, then the original value stays in the box.
Or in another example: I can tell you how much is on my bank account, which you can then use to calculate how much it is in dollars, or I can give you the right to change the amount on my bank account, in which case, the amount will probably change.

scanf() revisited

With this in mind, we can take a look again at how we used the function scanf() for getting input from the user. Remember that we always had to use the form
  scanf("%d", &i);
Or, in other words, we always had to give the address of (&) the variable (i) to scanf. Now it makes sense. We want to change the value of i with the function scanf. Therefore, we have to use the technique of passing by reference. Therefore, we have to give the address of i, rather than the value of i to scanf.

Example: Carthesian to polar coordinates

As we know, functions can only return a single value. What if we want to return more than one value to the calling part of the program? Take for example the conversion from Carthesian to polar coordinates. As input we have a coordinate (x, y) and as output we have a Carthesian coordinate (r, q). These are two values, one value for the radius (r) and one for the angle (q). The following solution shows how we can pass this information without using global variables:
 
#include <stdio.h>
#include <math.h>

void convert_to_polar(float x, float y, float *r, float *theta)
/**********************************************************
 * function to convert carthesian coordinate (x,y) to     *
 * polar coordinate (r,theta)                             *
 * parameters:                                            *
 *    x,y: floats, passed by value                        *
 *    r, theta: (pointers to) floats, passed by reference *
 *                  (changes are permanent)               *
 **********************************************************/
{
  *r = sqrt(x*x + y*y);
  *theta = atan(y/x);
}

void main()
{
  float rl, thetal;

  convert_to_polar(1.0, 1.0, &r1, &theta1);
  printf("polar coordinate is (%f, %f)\n", rl, thetal)
}
output:
  polar coordinate is (1.4142, 0.78539)


Type mixing

When passing information to functions we have to take even more care that we don't do any mixing of types, especially when we use pointers. As a (bad) example:
  void double10(double *dp)
  {
    *dp = 10.0;
  }

  void main()
  {
    float y=10.0;
    float x=10.0;

    double10(&x);
    printf("%f %f\n", x, y);
  }
A double occupies 8 bytes. Assigning a value to the contents of a pointer-to-double will therefore write in 8 consecutive bytes of memory. The pointer passed to the function is of type pointer-to-float, however. We have (by declaring the variable x of type float) only reserved 4 bytes of space in memory. The instruction *dp = 10; will now write in these 4 bytes and the 4 bytes next to it (that don't belong to x but to y)
The output of the program above (Borland C++ 3.01 for MS-DOS):
  0.000000 2.562500
Always avoid mixing of type:

Give to the function what the function wants


Quick Test

To test your knowledge of what you have learned in this lesson, click here for an on-line test.

Peter Stallinga. Universidade do Algarve, 19 November 2002