ProgramGuru

C Scope, Storage Duration and static Locals

C Scope, Storage Duration and static Locals

A ticket helper needs to remember the previous ticket number, while each call also needs temporary calculations. Visibility and lifetime answer different questions.

Scope determines where an identifier can be used. Storage duration determines how long an object exists. An automatic local usually belongs to one call; a static local persists for the whole program execution.

  1. Explain

    Distinguish an object’s lifetime from the places where its name is visible.

  2. Trace

    Make three successive calls and predict all ticket numbers.

  3. Apply

    Distinguish where a name is visible from how long its value exists.

Scope and Lifetime: follow the data

  1. Start

    A ticket helper needs to remember the previous ticket number, while each call also needs temporary calculations. Visibility and lifetime answer different questions.

  2. Operation

    Scope determines where an identifier can be used. Storage duration determines how long an object exists. An automatic local usually belongs to one call; a static local persists for the whole program execution.

  3. Result

    Ticket: 1 Ticket: 2

A guided model of this example. The illustration explains the process; run the complete C program to check its output.

Understand the operation

last is visible within next_ticket but persists across completed calls. Its initializer supplies the starting value once rather than resetting the number on every call. A plain automatic int last = 0 would be recreated for each call.

The static keyword has another role at file scope: it can give a function or object internal linkage, limiting that name’s linkage to the translation unit. Do not confuse that visibility/linkage use with the local lifetime example.

Read the complete C program

This complete C17 program uses a small school example. Read declarations before the statements that use their values. Each printf format describes how the corresponding value is written to standard output.

C17 · complete program

#include <stdio.h>
static int next_ticket(void) {
  static int last = 0;
  last++;
  return last;
}
int main(void) {
  printf("Ticket: %d\n", next_ticket());
  printf("Ticket: %d\n", next_ticket());
  return 0;
}

Expected output

Ticket: 1
Ticket: 2
Run in C compiler →

Follow the values through the code

C17 · code used in this explanation

#include <stdio.h>
static int next_ticket(void) {
  static int last = 0;
  last++;
  return last;
}
int main(void) {
  printf("Ticket: %d\n", next_ticket());
  printf("Ticket: %d\n", next_ticket());
  return 0;
}

The first call increments the persistent last from zero to one.

After it returns, last still exists; the next call increments the same object to two.

Follow the worked calculation

  1. Read the starting values

    The first call increments the persistent last from zero to one.

  2. Perform the operation

    After it returns, last still exists; the next call increments the same object to two.

  3. Check the observable result

    Ticket: 1 Ticket: 2

    Ticket: 1
    Ticket: 2
A guided model of this example. The illustration explains the process; run the complete C program to check its output.

Try a small variation

C17 · code used in this explanation

#include <stdio.h>
static int next_ticket(void) {
  static int last = 0;
  last++;
  return last;
}
int main(void) {
  printf("Ticket: %d\n", next_ticket());
  printf("Ticket: %d\n", next_ticket());
  return 0;
}

Make three successive calls and predict all ticket numbers.

Worked answer

Each loop iteration calls the helper once. The static object retains the previous value between iterations, unlike the loop’s automatic counter.

C17 · complete program

#include <stdio.h>
static int next_ticket(void) {
  static int last = 0;
  last++;
  return last;
}
int main(void) {
  for (int i = 0; i < 3; i++) printf("Ticket: %d\n", next_ticket());
  return 0;
}

Expected output

Ticket: 1
Ticket: 2
Ticket: 3
Run in C compiler →

Recognize the important boundaries

Persisting a counter does not make its updates thread-safe. Shared mutable objects need a concurrency design when multiple threads can access them.

Do not return an automatic local object’s address. Leaving its scope and ending its lifetime are concepts that need to be checked together.

Check your understanding

Choose the answer, then compare the reasoning for every option.

Does a static local initializer reset last on every call?
Choose one answer. You can try again.

Answer and explanation

No; the object persists across calls.

Correct. The example initializes it once and then updates the same object.

Use the idea independently

Scope determines where an identifier can be used. Storage duration determines how long an object exists. An automatic local usually belongs to one call; a static local persists for the whole program execution.

Continue with linkage and module contracts.

Language reference: WG14 C11 committee draft. These examples use the C17-compatible subset of the explained language features.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: C Pointers, Addresses and Dereferencing →