Lesson #15

Scope. Local (Block) vs Global

Overview

In this lesson I will cover the following topics

  1. Local scope and global scope.

  2. Variable hiding.

  3. Why global variables should be avoided and how that can be accomplished.

Body

Local scope

The topic of scope is important so read carefully!

So far in my examples I have declared variables just inside the opening brace of main. In the last lesson I showed how you can create and use other functions besides main. In those examples I declared some variables inside main and some inside the other function. I did this on purpose. To understand the significance of this, take a look at the following example.

#include <stdio.h>

// The function named 'f' takes no parameters and returns nothing.
void f(void)
{
  int i;

  i = 5;
}

// The main function.
int main(void)
{
  int i;

  i = 2;
  printf("i = %d\n", i);
  f();
  printf("i = %d\n", i);

  return 0;
}

As always, the program starts with main. Inside main a variable named i is declared and the value 2 is assigned to it. The first printf prints

i = 2

Next, the function f is called. Inside f another variable named i is declared and the value 5 is assigned to it. Nothing more is done inside f (making it a rather strange function) so f returns back to main. What does the second printf display? The answer is

i = 2

just like before. This is because the i inside function f has nothing to do with the i inside main. They are two different variables entirely. The main function does not know about the i inside function f. When you talk about i in main you are talking only about main's i. As far as main is concerned the variables inside f don't exist.

You might think that perhaps this works because f has its own declaration of i. Perhaps in the following example things are different:

#include <stdio.h>

// The function named 'f' takes no parameters and returns nothing.
void f(void)
{
  i = 5;
}

// The main function.
int main(void)
{
  int i;

  i = 2;
  printf("i = %d\n", i);
  f();
  printf("i = %d\n", i);

  return 0;
}

The only difference here is that f no longer tries to declare i. Instead it just assigns the value of 5 to i. Which i is it using? The one in main? No. This program is in error and it won't even compile. When the compiler sees me trying to assign to i in function f it will complain by saying that i is an "undefined identifier". In other words, there is no declaration for that i and the compiler doesn't know what it is. The compiler will not assume that it has anything to do with the i in main.

Remember: Variables declared inside a function are said to be "local" to that function and are said to have "local scope". They do not exist outside of the function.

This rule is very important. It means that you can use functions correctly without knowing what variables those functions use. Take a look at this program.

#include <stdio.h>

int main(void)
{
  int age;

  // Get the user's age.
  printf("What is your age? ");
  scanf("%d", &age);

  // Now print it out.
  printf("I understand you to be %d years old.\n", age);

  return 0;
}

Now suppose that, for some reason, the printf function used a variable named age internally (keep in mind that printf is probably also written in C). So what? It doesn't matter what variables printf is trying to use or what it's trying to do with them. When I say age I'm talking about the age variable that I declared in my main. Any age variables declared inside any of the other functions I'm using are different and won't interfere.

Without this rule it would be next to impossible to write a large program. Because of this rule two things are true.

  1. You don't need to worry about what local variables are being used by the functions you call. The variables you use are the ones you declare. You won't accidently use a variable that some other function is trying to use.

  2. When you write a function you can choose whatever variable names you like. You don't need to worry about accidently changing variables somewhere else in the program.

When a program is created by many different people, the last thing you want to worry about is coordinating variable names. And... you don't have to. You can pick whatever names you like and the other people can pick whatever names they like. There will be no clashes as long as all your variables have local scope (declared inside functions).

Global scope

As you can probably guess there is an alternative to local scope. It is called global scope. Such variables can be accessed by every function in the program. Here is an example

#include <stdio.h>

// Global variables are declared outside of all functions.
int i;

void f(void)
{
  // Here i is not "undefined." The value 5 is assigned to the global.
  i = 5;
}

int main(void)
{
  // Program starts by assigning 2 to the global.
  i = 2;
  printf("i = %d\n", i);  // Prints i = 2
  f();                    // Function f modifies the global.
  printf("i = %d\n", i);  // Prints i = 5

  return 0;
}

This program is very much the same as my earlier example. However, in this case the assignment of 5 to i in function f is not an error. The compiler has seen the global declaration of i and thus knows that the i in function f is the global i. When the program starts, it assigns 2 to the global i and then prints

i = 2

Next it calls function f which changes the value stored in the global i. When main resumes the next printf prints the new value

i = 5

In my earlier examples there were two different local variables named i. But there is only one global variable of any given name.

When you declare a variable outside of all functions it can be used inside any function that is defined after that variable's declaration. Typically people put global variable declarations at the top of their program so that every function in the program can use it. For example

#include <stdio.h>

int i;  // This is where global variables are usually declared.

void f(void)
{
  i = 5;  // Fine. Changes the global i.
  j = 7;  // Error. What is j?
}

int j;  // This is also a "global" but it can only be used from here down.

void g(void)
{
  i = 5;  // Fine. Changes the global i.
  j = 7;  // Fine. Changes the global j.
}

Mixing global and local variables

You can have different local variables with the same name as long as they are in different functions. However, there can be only one global variable with a particular name. You might now wonder what happens when you try to create a local variable with the same name as a global variable. This example shows the effect

#include <stdio.h>

int i;  // Global variable.

void f(void)
{
  i = 5;  // Sets the global i to 5.
}

int main(void)
{
  int i;  // Fine. Local variable named i is different than the global.

  i = 2;                  // Sets the local i to 2.
  printf("i = %d\n", i);  // Prints i = 2
  f();                    // Function f changes the global i.
  printf("i = %d\n", i);  // Prints i = 2

  return 0;
}

Inside function main any use of the name i refers to the local variable. The global variable is "hidden" by the local and, in effect, does not exist. However, in other functions without any local i, using the name i refers to the global variable. This is exactly what you want. When you declare a local variable and use it, you don't have to worry about similarly named local variables in other functions OR global variables. This gives you control over what you are doing. When you create and use a local variable you are getting exactly what you expect.

Actually you can declare variables inside any block. Such variables are local to that block and hide any similarly named variables outside of that block. This is not often done in C (it is more common in C++) but here is how it looks.

#include <stdio.h>

int i = 2; // Declare a global variable and initialize it to 2.

int main(void)
{
  int i;   // Local declaration hides the global variable.

  // Loop ten times. The value of the global i is not changed by this.
  for (i = 0; i < 10; i++) {
    int i = 5;  // Declare a new i local to this block and initialize it.

    printf("i = %d\n", i); // Prints i = 5 all the time.
  }

  // Print the value of the i declared in main (currently 10).
  printf("i = %d\n", i);

  return 0;
}

The global scope covers the entire program. Each block that you create with braces creates a new, inner scope into which you can optional declare variables. These inner declarations exist only in the block in which they are declared and they hide any similarly named variables declared in an enclosing block. Read this paragraph twice. This principle is very important.

It sometimes helps to turn things around a bit. When you use a variable the compiler needs to locate a declaration for that variable so that the compiler knows the type of the variable and so forth. The compiler starts its search in the innermost block where you used the variable. If it doesn't find a declaration there, it expands its search to the block enclosing the innermost block. The compiler continues in this way, expanding the search outwards block by block looking for a declaration. The compiler will even search outside all functions as a last ditch effort to locate the declaration. This final "block" is the global scope. If it can't locate a declaration for the variable even there, it gives up and calls the variable undefined.

And the point is?

Local variables have the very desirable property that they are not influenced by things happening in other parts of your program. Considering that most programs are written by large teams of people this is a very good thing. When you write a function you don't want to worry about the variables other people have created or will create.

However, there are times when two different functions want to share information. In that case, it is sometimes handy to have both of those functions manipulate a common global variable. Global variables definitely have their place. However, global variables "connect" two otherwise independent functions and thus make it more difficult to modify one of the functions without "breaking" the other. In general you should not use global variables! Global variables are useful only for certain, specialized situations. You should avoid them unless you absolutely know what you are doing.

Some students resort to using global variables when they should use function parameters instead. Let me show you a very simple example of that first. In the program below, I'm using a function to print out the result of a calculation. This is a bit silly, but I will show a more juicy example later.

#include <stdio.h>

int result;  // Global variable.

/*
 * This function takes no parameters and returns nothing. However it does
 * access the global variable Result.
 */
void print_result(void)
{
  printf("The result = %d\n", result);
}

int main(void)
{
  int age;

  // Ask the user for his/her age and store it in a local variable.
  printf("What is your age? ");
  scanf("%d", &age);

  // Compute age/2 (rounded up) and store in the global variable.
  result = (age + 1) / 2;

  // Call the function that prints the global variable.
  print_result();

  return 0;
}

Here I'm using a global variable to transmit information from one function to another. This is totally unnecessary. Sending information from one function to another is exactly what function parameters are for. Here is a version that does without the global variable. It is better.

#include <stdio.h>

// This function takes the value to be printed as a parameter.
void print_result(int the_result)
{
  printf("The result = %d\n", the_result);
}

int main(void)
{
  int age;
  int result;

  // Ask the user for his/her age and store it in a local variable.
  printf("What is your age? ");
  scanf("%d", &age);

  // Compute age/2 (rounded up) and store in a local variable.
  result = (age + 1) / 2;

  // Call the function that prints the results.
  print_result(result);

  return 0;
}

Because this program has no global variables the functions in it are more independent. The print_result function might be useful in another program. If so, you only need to copy and paste it from this program to the new one. Moving the print_result function in the original version would be harder. The global variable is like stringy cheese on a slice of pizza. It ends up dragging a whole bunch more with the function than you really want. You need the variable and you need all the stuff in main where it is given a value. Ugh.

The trick of using function parameters to pass information from one function to another works very well---provided one function calls another. Where global variables are useful is where two functions that don't call each other want to share information. There are tricks you can use to do without globals even in such cases, but there comes a point where the tricks are not worth the effort. Global variables are useful, but using them properly requires experience and caution. If you don't need to use them, then stay away from them. Declare everything local if you can.

Summary

  1. A variable declared inside curly braces is said to be "local" to the block defined by those braces. That variable can only be used in that block. A variable declared outside the bodies of all functions is said to be "global". It can be used in any function in the program.

  2. There can be many local variables with the same name in a single program. A local variable in one function will not conflict at all with a similarly named local variable in another function.

  3. If you declare a local variable with the same name as a global variable, the global variable is temporarly "hidden" by the local. Any use of that name in the block with the local variable will refer to the local variable. In this way, local variables also do not conflict with similarly named global variables.

  4. Global variables allow two functions to communicate information to each other (one function can set the value of the global variable and the other can use that value). However, this causes hard to manage dependencies to exist between functions. Modifying and reusing the functions is much more difficult. It is better for one function to pass information to another using function parameters instead of global variables. However, that technique only really works well when the two communicating functions are such that one calls the other.

© Copyright 2003 by Peter C. Chapin.
Last Revised: July 16, 2003