G27
Self-checkedTier 1 10 min readpds.scope-storage-classes

Scope, lifetime and storage classes

Three related but distinct ideas that GATE mixes together. Where a name is visible (scope), how long the object exists (lifetime) and who else can link to it (linkage), plus what auto, static, extern and register change.

Why it exists

Two questions sound similar but are different. Where in the source can I use this name? (scope) and how long does the thing it names exist while the program runs? (lifetime). A local variable is visible only inside its block and normally exists only while that block runs, so the two coincide and people forget they are separate. Storage classes such as static and extern pull them apart, and many GATE questions on this topic are about a variable whose scope and lifetime differ.

Prerequisites

Precise definitions

Scope is the part of the program text in which a name can be used. C has four kinds, two of which matter for GATE:

  • Block scope. A name declared inside { ... } (including function parameters and the variable of a for header) is visible from just after its declarator to the end of that block.
  • File scope. A name declared outside every function is visible from just after its declarator to the end of the translation unit (the source file after preprocessing).
  • Function prototype scope (parameter names in a declaration) and function scope (labels) are rarely tested.

Shadowing. A name in an inner block hides the same name from an outer block (or from file scope) until the inner block ends. C uses static (lexical) scoping: which declaration a name refers to is decided by the program text, never by who called whom.

Lifetime (storage duration) is the part of the run during which the object exists.

  • Automatic: created when execution enters the block, destroyed when it leaves. Default for locals. A fresh copy each time, holding garbage unless initialised.
  • Static: exists for the whole run and is initialised once, to zero (or null pointers) unless you give a constant initialiser. Globals and static locals. (C11 also has thread duration, _Thread_local, which GATE does not need.)
  • Allocated: from malloc to free (see the memory layout page).

Linkage says whether the same name in different scopes or files refers to the same object or function: external (shared, the default for file-scope functions and variables), internal (private to the file, static at file scope), or none (block-scope locals without extern, parameters, and names such as typedefs and labels).

Storage-class specifiers:

SpecifierAt file scopeInside a function
(none)external linkage, static durationauto: automatic duration, no linkage
autonot allowedthe default for locals; almost never written
staticinternal linkage (private to this file), static durationstatic duration: one persistent copy, still local in scope
externa declaration with external linkage (internal if an earlier static declaration is visible). It is only a declaration unless it has an initialiser, which makes it a definitiondeclares that the name refers to an object or function with linkage defined elsewhere; with an initialiser it is a constraint violation
registernot allowedhint to keep it in a CPU register; you may not take its address (&x is an error)

Initialisation rules.

  • Objects with static duration (globals and static locals) are initialised once, before the program starts. The initialiser must be a constant expression (static int a = f(); is an error in C).
  • Automatic objects are initialised each time the declaration is executed. Without an initialiser their value is indeterminate.

How it works

Step through three calls to the same function. Watch what disappears and what stays. The frame for counter holds the automatic a, which is created at 0 and vanishes on return. The globals and statics sit outside the stack in the data area and keep counting. Amber marks a value that has just changed.

Automatic, static and global variables

Real run, real addresses

C source

1int g = 0;
2int counter(void) {
3 static int s = 0;
4 int a = 0;
5 a++; s++; g++;
6 return s;
7}
8int main(void) {
9 counter();
10 counter();
11 counter();
12 return 0;
13}

Stack framesnewest on top

main()

no variables yet

Globals and statics (data area, not on the stack)

g0…8060

Step 1

Before any call: g exists (initialised to 0). The static s will join once counter runs.

1 / 10
a is created fresh on every call. s and g live outside the stack and keep their values between calls. Produced by: real execution of an instrumented C program (variables register themselves; every value and address is read from memory at each probe). gcc (Debian 14.2.0-19) 14.2.0. gcc -O0 -o frames-storage frames-storage.c && setarch x86_64 -R ./frames-storage

The rule to remember: in the usual implementation, only automatic variables live in frames. static locals have the scope of a local but the lifetime of a global, which is exactly why they are tested.

Worked examples

EX 1Shadowing in nested blocks

Easy
#include <stdio.h>
 
int x = 1;
 
int main(void) {
    printf("%d ", x);
    {
        int x = 2;
        printf("%d ", x);
        {
            int x = 3;
            printf("%d ", x);
        }
        printf("%d ", x);
    }
    printf("%d\n", x);
    return 0;
}

Output:

1 2 3 2 1

Reading it. Each inner x hides the outer one only while its block is running. When a block ends, the previous x is visible again, with the value it had before. The global x is never changed.

EX 2Static against automatic inside a loop

Medium
#include <stdio.h>
 
int main(void) {
    for (int i = 0; i < 3; i++) {
        static int s = 0;
        int a = 0;
        s += 2;
        a += 2;
        printf("%d %d  ", s, a);
    }
    printf("\n");
    return 0;
}

Output:

2 2  4 2  6 2

Step 1. static int s = 0; is initialised once, not on every loop pass. So s goes 2, 4, 6.

Step 2. int a = 0; is executed on every pass, so a starts at 0 each time and ends each pass at 2.

EX 3A static local initialised once

Medium
#include <stdio.h>
 
int f(void) {
    static int n = 10;
    n += 5;
    return n;
}
 
int main(void) {
    int first = f();
    int second = f();
    printf("%d %d\n", first, second);
    return 0;
}

Output:

15 20

Why not 15 15. The initialisation = 10 is not an assignment that runs on every call. It happened once, before main. After that, each call just adds 5 to the surviving value.

EX 4Global against local with the same name

GATE level
#include <stdio.h>
 
int i = 10;
 
void f(void) {
    int i = 5;
    i++;
    printf("%d ", i);
}
 
int main(void) {
    f();
    f();
    i++;
    printf("%d\n", i);
    return 0;
}

Output:

6 6 11

Step by step. In f the local i hides the global i. Each call creates a new local set to 5, increments it to 6 and prints it, so the first two numbers are both 6. In main there is no local i, so i++ changes the global from 10 to 11.

EX 5Static versus dynamic scoping (not C)

GATE level

C is statically scoped, but GATE (and compiler courses) also ask what a program would print under dynamic scoping, where a non-local name is looked up in the caller's variables at run time. Take this program in pseudocode:

x = 1
procedure f()  { print x }
procedure g()  { local x = 2;  call f() }
call g()
call f()
  • Static scoping (C, Java, Python): f was written where only the global x is visible, so f always prints the global x. Output: 1 then 1.
  • Dynamic scoping: f uses the most recent still-active binding of x along the call chain. Called from g, the most recent x is g's local, so it prints 2. Called from the top level it prints 1. Output: 2 then 1.

You can check this for real in bash, whose local variables are dynamically scoped: running the same structure there prints exactly f sees x = 2 and then f sees x = 1. In C the equivalent program prints 1 and 1, because f can only see the global.

Cheat box

Cheat box

  • Scope = where the name is visible (text). Lifetime = how long the object exists (run time). Linkage = whether other files can refer to it.
  • Locals: block scope, automatic lifetime, new copy per call, garbage if uninitialised.
  • Globals and static locals: static lifetime, initialised once, zero by default.
  • static inside a function: local scope, global lifetime. static at file scope: file-private (internal linkage).
  • extern alone declares, it does not define (an initialiser makes it a definition). register: you cannot take the address.
  • Inner declarations shadow outer ones until their block ends.
  • C is lexically scoped: the declaration is chosen by the text, not the call chain.
  • static initialiser: a constant expression, evaluated once.

Traps and GATE patterns

Connections

Where each kind of variable physically lives is on the memory layout page. A compiler implements these rules with a symbol table for scope and with activation records for automatic storage, and the way recursion keeps a fresh copy per call is explained in recursion and the call stack.

PYQs

8 previous year questions from your book. I re-solved each one myself (8 by running or simulating, 0 by reasoning only); the book's key is shown next to my answer. All PYQs

GATE 2019 · MCQ · 2 marksQ5 · page 11 of the book
Consider the following C program:
    #include <stdio.h>
    int r ( )
{
    static int num=7;
    return num--;
}
 int main ( )
{
 for (r ( ); r ( ); r ( ))
   printf ("%d", r ( ));
 return 0;
}
Which one of the following values will be displayed
on execution of the programs?
  • (a)41
  • (b)63
  • (c)52
  • (d)630
GATE 2026 · Set 2 · MCQ · 1 markQ1 · page 17 of the book
Consider the following three ANSI – C programs. P1,
P2, and P3

P1
P2
P3
#include
<stdio.h>

int a = 5;
int main(){
int a = 7;
return(0);
}
#include
<stdio.h>

int main(){
int a=5;
int a=7;
return(0);
}
#include
<stdio.h>

int main(){
int a=5;
float a=7;
return(0);
}

Which one of the following statement is true?
  • (a)Only P1 will compile without any error
  • (b)Only P2 will compile without any error
  • (c)Only P3 will compile without any error
  • (d)All three programs P1, P2, and P3 will compile without any error
GATE 2023 · Set 1 · NAT · 1 markQ7 · page 18 of the book
Consider the following program in C:

#include <stdio.h>

void func(int i, int j) {

if (i < j) {
       int i = 0;
       while (i < 10) {
           j += 2;

    i++

}
       }

  printf("%d", i);
}

int main() {

   int i = 9, j = 10;

func(i, j);

return 0;

}
The output of the program is ______. (answer in
integer)
Note: Assume that the program compiles and runs
successfully.
GATE 2020 · NAT · 2 marksQ12 · page 19 of the book
Consider the following C functions.

The return value of fun2(5) is ____
GATE 2017 · Set 1 · NATQ19 · page 21 of the book
The output after executing the following program is
___

#include <stdio.h>

int total(int v) {
    while (v) {
        static int count += v & 1;
        v >>= 1;
    }
    return count;
}

void main() {
    static int x = 0;
    int i = 5;
    for (; i > 0; i--) {
        x = x + total(i);
    }
    printf("%d\n", x);
}
GATE 2016Q23 · page 22 of the book
void count(int n){
    static int d = 1;
    printf("%d ", n);
    printf("%d ", d);
    d++;
    if(n > 1) count(n - 1);
    printf("%d ", d);
}

void main(){
    count(3);
}
  • (a)312213444
  • (b)312111222
  • (c)3122134
  • (d)3121112
GATE 2015 · Set 3 · NATQ25 · page 22 of the book
Consider the following C program. The output of the
program is __________.
# include <stdio.h>
int f1(void);
int f2(void);
int f3(void);
int x = 10;
int main()
{
 int x = 1;
 x += f1() + f2() + f3() + f2();
printf("%d", x);
 return 0;
 }
int f1()
{
int x = 25;
 x++;
return x;
}
int f2( )
{
 static int x = 50;
 x++;
return x;
}
int f3( )
{
 x *= 10;
 return x;
}
GATE 2012 · MCQ · 2 marksQ32 · page 24 of the book
Consider the following C code segment.
int a, b, c = 0;
void prtFun (void);
main ()
{
static int a = 1;
prtFun ();
a + = 1;
prtFun ();
printf("\n %d %d ", a, b);
}

void prtFun (void)
{
static int a = 2;
int b = 1;
a + = + + b;
printf (" \n %d %d ", a, b);
}
What output will be generated by the given code
segment?
  • (a)31 41 42
  • (b)42 61 61
  • (c)42 62 20
  • (d)42 42 20

Self-check

Question 1

What does this print?

#include <stdio.h>
 
int count(void) {
    static int c = 0;
    return ++c;
}
 
int main(void) {
    int a = count();
    int b = count();
    int c = count();
    printf("%d %d %d\n", a, b, c);
    return 0;
}
Show answer

The static c inside count survives; the c in main is a different variable.

1 2 3

Question 2

Can a function return a pointer to its static local variable safely? What about to an ordinary local?

Show answer

A static local exists for the whole run, so a pointer to it stays valid. An ordinary local is destroyed when the function returns, so a pointer to it is dangling and using it is undefined.

Question 3

What does this print, and why is the answer not 5?

#include <stdio.h>
 
int v = 1;
 
void bump(void) {
    int v = 5;
    v = v + 1;
}
 
int main(void) {
    bump();
    printf("%d\n", v);
    return 0;
}
Show answer

The local v in bump hides the global, so bump only changes its own copy. The global is untouched:

1

Question 4

Under dynamic scoping, in the pseudocode of Example 5, what would call f() print if g is changed to set x = 2 without the word local?

Show answer

Then g modifies the one and only (global) x, so both f calls print 2 under either scoping rule. The difference between the two scoping rules only appears when a local declaration shadows a global one.

Revision card

One-screen revision

  • Scope is textual, lifetime is run-time, linkage is across files. Keep them separate.
  • Automatic locals: new per call, garbage by default. Static and global: one copy, zero by default, initialised once.
  • static local = local name, global lifetime. static at file scope = private to the file.
  • extern int x; declares only (with an initialiser it defines). register forbids &.
  • Inner names shadow outer ones; C chooses by text (static scoping).
  • Dynamic scoping looks along the call chain and gives different answers when a local shadows a global.

References

  • ·Kernighan and Ritchie, The C Programming Language, 2nd ed., Sections 1.10 External Variables and Scope, 4.3 External Variables, 4.4 Scope Rules, 4.6 Static Variables, 4.7 Register Variables, 4.8 Block Structure, 4.9 Initialization
  • ·ISO/IEC 9899:2011 (C11), Sections 6.2.1 Scopes of identifiers, 6.2.2 Linkages of identifiers, 6.2.4 Storage durations of objects, 6.5.3.2 Address and indirection operators, 6.7.1 Storage-class specifiers, 6.7.9 Initialization, 6.9 External definitions
  • ·Aho, Lam, Sethi, Ullman, Compilers: Principles, Techniques, and Tools, 2nd ed., Section 1.6.3 Static Scope and Block Structure, Section 1.6.5 Dynamic Scope

Verification

The animation replays a real run of an instrumented C program (every value is read from memory at each step). All C programs on this page are compiled and run by npm run verify:c. The dynamic-scoping example was checked by running the same function structure in bash, which uses dynamic scoping. The prose, the linkage rules and the GATE-pattern claims have NOT yet had a human textbook check (the independent second pass is described at the end of these notes). SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand (long C programs were not re-read line by line: they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose). No defect found. Status raised to self-checked; verified needs a textbook or human check.

last verified: never