Scope is about where in a program you can use a variable. A variable might only be usable inside one function, or it might be usable everywhere in the file.
There are two kinds you will see first:
- Local scope variables (local variables)
- Global scope variables (global variables)
Local scope
A variable defined inside a function is local to that function. Nothing outside can see it.
#include <stdio.h>
int main() {
int rpm = 900;
printf("rpm is %d\n", rpm);
return 0;
}Here rpm only exists inside main. Its scope is the curly brackets of the function.
Using a local variable from another function
Now add a second function that tries to change rpm:
#include <stdio.h>
void revUp(void);
int main() {
int rpm = 900;
printf("main: rpm is %d\n", rpm);
revUp();
return 0;
}
void revUp(void) {
rpm = 800;
printf("revUp: rpm is %d\n", rpm);
}main.c: In function 'revUp':
main.c:14:2: error: 'rpm' undeclared (first use in this function)revUp has no idea what rpm is. That variable belongs to main, so revUp can’t touch it. You can use a local variable anywhere inside its own function, but not outside of it.
Note
The
void revUp(void);line near the top is a function prototype. It tells the compiler the function exists before it sees the body. We will cover that in a later note.
Global scope
If several functions need the same variable, move it out of the function and into the global scope, which is the space outside every function.
#include <stdio.h>
int rpm = 900;
void revUp(void);
int main() {
printf("main: rpm is %d\n", rpm);
revUp();
printf("main again: rpm is %d\n", rpm);
return 0;
}
void revUp(void) {
rpm = 800;
printf("revUp: rpm is %d\n", rpm);
}main: rpm is 900
revUp: rpm is 800
main again: rpm is 800Now every function in this file can see rpm. The order of events:
mainprints 900.maincallsrevUp.revUpchanges the globalrpmto 800 and prints it.- Control goes back to
main, which prints 800, because it was the same variable all along.
Same name in local and global scope
You can use the same name in both places. The compiler is fine with it because they live in different scopes.
#include <stdio.h>
int limit = 6000;
int main() {
int limit = 7000;
printf("limit is %d\n", limit);
return 0;
}limit is 7000The local variable wins. When the names match, the compiler always looks in the local scope first. The local limit hides the global one. This is called shadowing.
What you cannot do is define two variables with the same name in the same scope:
int limit = 6000;
float limit = 7000;error: conflicting types for 'limit'Block scope
Curly brackets alone make a scope. You don’t need a function for it.
#include <stdio.h>
int rpm = 4000;
int main() {
{
int rpm = 7000;
printf("inside block: %d\n", rpm);
}
printf("outside block: %d\n", rpm);
return 0;
}inside block: 7000
outside block: 4000The inner rpm only lives between its own brackets. Once the program leaves the block, that variable is gone, so the second printf finds the global rpm.
Important
When execution leaves the scope of a local variable, the variable dies. It no longer exists.
Try to predict the output of this program before reading on.
#include <stdio.h>
int rpm;
int main() {
rpm = 2500;
{
int rpm = 4500;
printf("inside block: %d\n", rpm);
}
printf("outside block: %d\n", rpm);
int boost;
boost = boost + 10;
printf("boost: %d\n", boost);
return 0;
}Walking through it
rpm is a global variable. Inside main, the first line sets it to 2500.
Then a block starts with curly brackets, and a new local rpm is defined inside it with the value 4500. This is legal because the local one lives in a different scope. Inside the block, the local rpm hides the global one, so the first printf prints 4500.
When the block ends, the local rpm dies. The next printf sees the global rpm again, which is still 2500.
So the first two lines of output are:
inside block: 4500
outside block: 2500The uninitialized variable
Next we define boost without giving it a value, then run boost = boost + 10.
In an assignment, the right hand side is evaluated first. So the program reads boost, adds 10, and stores the result back into boost. But boost never had a value, so it holds whatever garbage was already sitting in that memory. Garbage plus 10 is still garbage, and the last line is unpredictable.
boost: 32778Your number will be different, and it may be different on every run.
Warning
Local variables are not initialized automatically. If you don’t give them a value, they contain random leftover data.
You might get lucky and see 10, because the leftover value happened to be 0. Don’t rely on that. It can change with the compiler, the optimization level or even the machine.
Let the compiler warn you
Compile with warnings turned on:
gcc -Wall main.c -o mainmain.c: In function 'main':
main.c:17:10: warning: 'boost' is used uninitialized [-Wuninitialized]The compiler is telling you that you read a variable before giving it a value. This one is harmless in a tiny example, but the same mistake in a bigger program causes bugs that are very hard to track down. Treat this warning seriously.
The fix is to always give a local variable a starting value:
int boost = 0;Global vs local defaults
Global variables are different. They start at 0 if you don’t initialize them. That is why int rpm; at the top of the program was safe even before we assigned 2500.
| Global variables | Local variables | |
|---|---|---|
| Scope | Visible to all functions in the program | Only inside the body of the function (or block) where they are defined |
| Access from another file | Yes, by declaring it there with extern | No |
| Default value | 0 if not initialized | Unpredictable, also called a garbage value |
| Lifetime | Until the program finishes executing | Until the function in which they are defined finishes executing |