C Tutorial
C Scope
A name is visible from its declaration to the end of its block. Inner names can hide outer ones.
Where a name is visible
Scope is the region of the program where a name can be used. In C, a variable declared inside a block — a pair of braces { } — is visible from its declaration to the end of that block. After the closing brace, the name is gone.
File-scope names (declared outside every function) are visible from their declaration to the end of the file. Function parameters are visible for the whole function body. This chapter stays with block scope and hiding, which is what trips people up first.
Block scope
The inner n exists only between its declaration and the end of the inner block. Theprintf after that block uses the outer n again. The inner value is not waiting there; that object ended with its block.
Example
#include <stdio.h>
int main(void) {
int n = 2;
printf("outer: %d\n", n);
{
int n = 3;
printf("inner: %d\n", n);
}
printf("outer again: %d\n", n);
return 0;
}Output is 2, then 3, then 2. Two different integers happened to share a name. The inner one hid the outer one for a few lines, then disappeared.
Run this in /c/try. gcc compiles one .c file there. Add another inner block with its own n and watch which value eachprintf sees.
Hiding
When an inner declaration uses a name that already exists in an outer scope, the inner name wins until its block ends. That is hiding (sometimes called shadowing). The outer object is still there. You just cannot reach it by that name while the inner one is in scope.
Example
#include <stdio.h>
int n = 1;
int main(void) {
printf("file: %d\n", n);
int n = 2;
printf("main: %d\n", n);
{
int n = 3;
printf("block: %d\n", n);
}
printf("main again: %d\n", n);
return 0;
}Output is 1, 2, 3, 2. The file-scope n is hidden for the whole of main after the local int n = 2. There is no C keyword that means “give me the outer n.” Pick different names if you still need both values.
A loop counter is a block too
In C17, for (int i = 0; ...) declares i in a scope that covers the loop. After the loop, i is not visible. Declare i before the for if you need the final index later.
| Kind | Visible where |
|---|---|
| File scope | From the declaration to the end of the file |
| Function / block | From the declaration to the closing brace of that block |
for-loop int i | The loop header and body, not after |
Prefer names that do not collide
Hiding is legal. It is also easy to misread. If the inner object is a different fact, give it a different name. Use hiding only when you truly want the inner name to take over for a short block. Next: command-line arguments, which arrive in main as argc and argv.
Worked examples
The short listings above are there so you can see the grammar. The programs here use the same statements on quantities that already have units: a speed, a pH, a count of bases. They are classroom numbers. Air resistance is ignored. g is 9.81 m/s² unless a line says otherwise.
Open them in the C editor at /c/try. Change one measurement and check whether the result still has the right unit.
Maths
A discriminant that dies with the block
x² − 3x + 2 = 0 has discriminant 1, roots 1 and 2. d is only needed for that check. Declaring it in an inner block keeps the name off the rest of main. After the closing brace, d does not exist.
Δ = b² − 4ac
Example
#include <stdio.h>
int main(void) {
double a = 1.0, b = -3.0, c = 2.0;
{
double d = b * b - 4.0 * a * c;
printf("discriminant = %.1f\n", d);
}
return 0;
}Physics
Time of flight, local
Thrown straight up at 20 m/s, time to return is 2u/g ≈ 4.08 s. That t is a one-off. Putting it in a block says it is not a setting for the whole program. Air resistance would shorten the time; this is the vacuum sketch.
T = 2u / g
Example
#include <stdio.h>
int main(void) {
double g = 9.81;
double u = 20.0;
{
double t = 2.0 * u / g;
printf("time of flight ~ %.2f s\n", t);
}
return 0;
}