Scope and static
Where a variable can be used is called its scope. So far, every variable has been declared inside a function, and can only be used in that function. Two functions can each have a variable with the same name, and the two have nothing to do with each other:
1#include <stdio.h>2 3void morning(void) {4 int steps = 3200;5 printf("Morning: %d steps\n", steps);6}7 8void evening(void) {9 int steps = 5100;10 printf("Evening: %d steps\n", steps);11}12 13int main(void) {14 morning();15 evening();16 return 0;17}
Morning: 3200 steps Evening: 5100 steps
Local variables
A variable declared inside a function is a local variable. It is created each time the function is called and is gone when the function ends. Other functions can't see it, and neither can main:
1#include <stdio.h>2 3void add_tax(int price) {4 int total = price + price / 10;5 printf("With tax: %d\n", total);6}7 8int main(void) {9 add_tax(200);10 printf("%d\n", total);11 return 0;12}
main.c: In function 'main':
main.c:10:20: error: 'total' undeclared (first use in this function)
10 | printf("%d\n", total);
| ^~~~~
main.c:10:20: note: each undeclared identifier is reported only once for each function it appears intotal lives only inside add_tax. In main, the compiler doesn't know the name at all: 'total' undeclared means "total was never declared". Parameters are local variables too: price can also only be used inside add_tax.
To hand the result to main, use return, as in the last lesson.
Variables in blocks
Not only functions: any pair of curly braces { } is a block. A variable declared inside a block can't be used once you are outside those braces:
1#include <stdio.h>2 3int main(void) {4 int hours = 10;5 if (hours > 8) {6 int extra = hours - 8;7 printf("Overtime: %d hours\n", extra);8 }9 printf("Total %d hours, overtime %d hours\n", hours, extra);10 return 0;11}
main.c: In function 'main':
main.c:9:58: error: 'extra' undeclared (first use in this function)
9 | printf("Total %d hours, overtime %d hours\n", hours, extra);
| ^~~~~
main.c:9:58: note: each undeclared identifier is reported only once for each function it appears inextra is declared in the if block, and line 9 is already outside it. To use it outside, declare it outside, for example with int extra = 0; before the if. The i in for (int i = 0; ...) works the same way: it exists only in that loop.
Global variables
A variable declared outside all functions is a global variable. Every function below its declaration can use it, and it lives from the start of the program to the end:
1#include <stdio.h>2 3int visitors = 0;4 5void welcome(void) {6 visitors++;7 printf("Welcome, you are visitor %d\n", visitors);8}9 10int main(void) {11 welcome();12 welcome();13 welcome();14 printf("%d visitors today\n", visitors);15 return 0;16}
Welcome, you are visitor 1 Welcome, you are visitor 2 Welcome, you are visitor 3 3 visitors today
welcome and main use the same visitors. Press Step to watch: a global variable isn't in any function's stack frame. It sits on its own, and when one function changes it, everyone sees the new value.
A global variable with no starting value is 0 automatically. A local variable is not: using one before giving it a value is a mistake (you saw this in lesson 2).
Shadowing
An inner scope can declare a variable with the same name as one outside. Inside, the name then means the inner variable, and the outer one is shadowed: hidden for now.
What does this print?
1#include <stdio.h>2 3int count = 100;4 5int main(void) {6 int count = 5;7 count++;8 printf("%d\n", count);9 return 0;10}
main declares its own count, so inside main, count is that local variable and the global count is shadowed. count++ turns the local 5 into 6. The global one is still 100; nothing touched it.
A block can shadow an outer variable too. Once the block ends, the outer one is back:
1#include <stdio.h>2 3int main(void) {4 int score = 70;5 if (score < 80) {6 int score = 80;7 printf("In the block: %d\n", score);8 }9 printf("Outside: %d\n", score);10 return 0;11}
In the block: 80 Outside: 70
Just because you can doesn't mean you should. Two variables with one name are easy to mix up; give them different names.
static local variables
An ordinary local variable is created again on every call, so nothing is kept from last time. The count below starts at 0 every time, so it always prints 1:
1#include <stdio.h>2 3void press(void) {4 int count = 0;5 count++;6 printf("Presses so far: %d\n", count);7}8 9int main(void) {10 press();11 press();12 press();13 return 0;14}
Presses so far: 1 Presses so far: 1 Presses so far: 1
Put static in front of the declaration and it becomes a static local variable. Only this function can use it (its scope is still local), but like a global it lives from the start of the program to the end. The = 0 happens once, at the very start; after that, each call finds the value from last time:
1#include <stdio.h>2 3void press(void) {4 static int count = 0;5 count++;6 printf("Presses so far: %d\n", count);7}8 9int main(void) {10 press();11 press();12 press();13 return 0;14}
Presses so far: 1 Presses so far: 2 Presses so far: 3
Press Step on both and compare. In the first, every call gives press a stack frame with a new count that starts at 0. In the second, count isn't in the stack frame: it stays when the function ends, and the next call keeps adding to it.
Use globals sparingly
Globals are handy, but anyone can change them. In a long program, when a value is wrong, you have to check every function to find out who changed it. This program is meant to print 3 rows of stars, but main and print_stars share one global i:
1#include <stdio.h>2 3int i;4 5void print_stars(int n) {6 for (i = 0; i < n; i++) {7 printf("*");8 }9 printf("\n");10}11 12int main(void) {13 for (i = 0; i < 3; i++) {14 print_stars(5);15 }16 return 0;17}
*****
Only 1 row. When print_stars finishes, i is already 5. Back in main, i++ makes it 6, i < 3 is false, and the loop ends. Press Step and keep an eye on i. Delete int i; and write for (int i = 0; ...) in both loops, so each has its own local variable, and it works.
Tip: Give a function the values it needs as parameters, and hand back its result with return. Then the first line of a function tells you what it uses and what it gives back.
Practice
Write the code, then press Check. A hint costs nothing; the answer is there when you need it.
Take a number
✓Each call to next_ticket should return the next number: 1, 2, 3... Right now it returns 1 every time. Fix it so the program prints Ticket 1, Ticket 2 and Ticket 3, each on its own line.
Ticket 1 Ticket 2 Ticket 3
1#include <stdio.h>2 3int next_ticket(void) {4 int number = 0;5 number++;6 return number;7}8 9int main(void) {10 printf("Ticket %d\n", next_ticket());11 printf("Ticket %d\n", next_ticket());12 printf("Ticket %d\n", next_ticket());13 return 0;14}
Shipping
✓This program doesn't compile. Fix it: orders of 100 or more ship free; otherwise shipping is 12. The program should print Shipping: 12.
Shipping: 12
1#include <stdio.h>2 3int main(void) {4 int amount = 85;5 if (amount < 100) {6 int shipping = 12;7 }8 printf("Shipping: %d\n", shipping);9 return 0;10}
Shopping cart
✓add adds a price to the global total, yet the program ends by printing Total: 0. Find out why and fix it, so the program prints Total: 60.
Total: 60
1#include <stdio.h>2 3int total = 0;4 5void add(int price) {6 total = total + price;7}8 9int main(void) {10 int total = 0;11 add(25);12 add(35);13 printf("Total: %d\n", total);14 return 0;15}
What you learned
- A variable's scope is where it can be used.
- Variables declared in a function or a block are local: they can only be used inside their own braces. Parameters are local too.
- Variables declared outside all functions are global: every function can use them, and they start at 0 if not given a value.
- An inner variable with the same name shadows the outer one; better not to name them that way.
- A
staticlocal variable can only be used in its own function, but it stays alive and remembers its value. - Use globals sparingly: pass in what a function needs as parameters and hand back the result with
return.