Skip to content

C Crash Course, Part 3: Variable Scope

This is part three of a C programming mini crash-course.

So you’ve learned some C programming and know how to split your program up into multiple files. But how do you communicate between files, and how do you store state between method calls?

Variable scope determines when a variable is visible in the code (i.e. when it can be used). Scope can be:

  1. Local
  2. Method
  3. File
  4. Global

In general, curly brackets always define a new scope. Like this:

#include <stdint.h>
#include <stdio.h>
int main(int argc, char** argv)
{
int16_t i;
int16_t varA = 0;
for (i = 0; i < 10; i++)
{
int16_t varB = 0;
varB += i;
varA += i;
printf("varB: %d\r\n", varB);
}
printf("varA: %d\r\n", varA);
return 0;
}

In the code above, varA is visible in all of main(), but varB is only visible inside the for-loop. If you try to move the line which prints varB outside of the for-loop, the code will not compile.

Remember the calculator? It looked like this:

#include <stdio.h>
float add(float left, float right)
{
float value;
value = left + right;
return value;
}
float sub(float left, float right)
{
float value;
value = left - right;
return value;
}
int main(int argc, char** argv)
{
float value = 0;
value = add(value, 10.5);
value = sub(value, 6.3);
printf("Calculator\r\n");
printf("Result: %f\r\n", value);
return 0;
}

The main(), add() and sub() methods all have a variable called value. These are completely different variables. The value in the add() method has nothing to do with the value in the sub() method — you could be tricked into thinking so, because the variables have the same name, but remember that curly braces define scope.

The parameters to a method also define variables which can be used in the method. The scope of the left and right parameters to the add() method is also limited to the method.

Variables can be defined at file scope. This means that all methods in the file have access to the variable. As a rule of thumb, limit the number of variables at file scope — the main use of file-scope variables is to remember state.

#include <stdint.h>
#include <stdio.h>
static uint16_t numberOfBullets;
void cannon_Load()
{
numberOfBullets = 10;
}
void cannon_Fire()
{
if (numberOfBullets > 0)
{
printf("Bang!\r\n");
numberOfBullets--;
}
else
{
printf("Out of bullets\r\n");
}
}

The code above is a cannon, implemented in Cannon.c. When it is loaded, it has 10 bullets — this is remembered in the state variable numberOfBullets. Each call to cannon_Fire() fires a bullet and modifies the state.

The keyword static in the numberOfBullets definition means that numberOfBullets is limited to file scope. No other file can access that variable.

If you take the code from the cannon above, remove the static keyword in front of numberOfBullets, and then add the same variable name to another .c file, some compilers will assume that the variable is the same:

Cannon.c
#include <stdint.h>
#include <stdio.h>
uint16_t numberOfBullets;
void cannon_Load()
{
numberOfBullets = 10;
}
void cannon_Fire()
{
if (numberOfBullets > 0)
{
printf("Bang!\r\n");
numberOfBullets--;
}
else
{
printf("Out of bullets\r\n");
}
}
CannonUser.c
#include <stdint.h>
#include <stdio.h>
uint16_t numberOfBullets;
int main(int argc, char** argv)
{
int16_t i;
cannon_Load();
for (i = 0; i < 20; i++)
{
cannon_Fire();
printf("NumberOfBullets: %d", numberOfBullets);
}
}

Luckily, most compilers will give an error or a warning that numberOfBullets has multiple definitions.

Noob programmers are sometimes tempted to use global variables to communicate between methods implemented in different files (and someone they thought was a friend told them about the extern keyword). Don’t do it! It is a path which leads to anger, hate, suffering, and ultimately the dark side.

Is there some scenario where global variables are justified? No. If you want other .c files to know how many bullets are left, the correct way to do it is to add a getNumberOfBullets() method to Cannon.c.