Placement Prep

Variable Scope in C: Block, File, and Storage Class Rules

C has four kinds of variable scope. This guide covers block scope, file scope, storage classes, and the output puzzles that appear in placement tests.

By FACE Prep Team 8 min read
c-programming variable-scope storage-classes placement-prep coding-interview block-scope static-variables

Variable scope in C tells the compiler which parts of a program can access a given identifier. The C11 standard §6.2.1 defines exactly four kinds: block scope, file scope, function scope, and function prototype scope.

This matters in placement tests because scope-tracing questions appear in TCS NQT, Infosys InfyTQ, and Wipro aptitude rounds. The output of a ten-line C snippet often depends on whether the candidate has correctly tracked which variable a name refers to at each point. Getting that wrong in an MCQ costs a mark; getting it wrong in production code can corrupt state across an entire run.

Scope, Lifetime, and Linkage: Three Separate Ideas

Most placement articles collapse these three concepts into one explanation, which is why candidates get confused when a test question separates them.

Scope is about visibility: in which region of source code is a name usable? It is a compile-time property.

Storage duration (often called lifetime) is about when memory is allocated and released. A variable can have automatic duration, where memory is freed when its enclosing block exits, or static duration, where memory persists for the entire program run.

Linkage is about whether a name in one translation unit refers to the same entity as the same name in another translation unit. External linkage means yes. Internal linkage means no. No linkage means the name is entirely local to its scope.

These three attributes combine independently. A global variable declared without any keyword has file scope, static storage duration, and external linkage by default. A static variable declared inside a function has block scope, static storage duration, and no linkage. A register variable declared inside a function has block scope, automatic storage duration, and no linkage. Understanding them separately is the only way to trace the output of a tricky scope question correctly.

The Four Scope Kinds in C

The C11 standard §6.2.1 enumerates exactly four.

Block Scope

Any variable declared inside a pair of braces {} has block scope. It is visible from the point of its declaration to the closing brace of that block, and nowhere else.

#include <stdio.h>

int main(void) {
    int x = 10;        /* block scope within main */
    {
        int y = 20;    /* nested block scope */
        printf("%d %d\n", x, y);  /* prints: 10 20 */
    }
    /* y is not accessible here — its block has closed */
    return 0;
}

Function parameters have block scope too. They are visible inside the function body and nowhere else. This is the scope most C programmers use daily without ever needing to name it.

File Scope

A variable or function declared outside any block has file scope. It is visible from its point of declaration to the end of the source file (translation unit). This is what most developers mean when they say “global variable.”

#include <stdio.h>

int counter = 0;   /* file scope */

void increment(void) { counter++; }

int main(void) {
    increment();
    increment();
    printf("%d\n", counter);   /* prints: 2 */
    return 0;
}

Function Scope

This is the entry that most placement tutorials get wrong. Function scope in C applies to one kind of identifier only: labels. A label (the target of a goto statement) is visible throughout its entire enclosing function, regardless of where in the function body it appears.

No other identifier has function scope. A variable declared inside a function, even at the very top of the function body, has block scope (the block being the function body), not function scope.

This distinction matters because placement MCQs occasionally describe a local variable as having “function scope.” That description is wrong according to C11. If a question asks which scope kind a plain local variable has, the correct answer is block scope, not function scope.

Function Prototype Scope

Parameter names in a function prototype (a declaration, not a definition) have function prototype scope. They exist only within the prototype itself, terminating at the closing parenthesis. The names used in the prototype are entirely optional; they do not need to match the names used in the function definition.

void display(int value);       /* prototype: 'value' has function prototype scope */

void display(int count) {      /* definition: 'count' has block scope */
    printf("%d\n", count);
}

The compiler only uses the types from the prototype, not the parameter names.

Storage Classes and How They Interact With Scope

C has four storage-class specifiers: auto, static, extern, and register. The cppreference storage-class reference has the full C11-aligned specification for each.

auto

auto is the default for any variable declared inside a block. You will almost never see it written explicitly in C source code because the compiler assumes it when no storage class is specified. The variable has automatic storage duration (allocated when its block is entered, released when it exits) and no linkage.

void example(void) {
    auto int x = 5;   /* identical to: int x = 5; */
}

static

static has two distinct effects depending on where it appears.

Inside a block, static gives the variable static storage duration while keeping block scope. The variable is initialized once, on the first time control reaches its declaration, and retains its value across every subsequent call to the enclosing function.

#include <stdio.h>

void countCalls(void) {
    static int count = 0;   /* initialized once; retains value across calls */
    count++;
    printf("Call number %d\n", count);
}

int main(void) {
    countCalls();   /* prints: Call number 1 */
    countCalls();   /* prints: Call number 2 */
    countCalls();   /* prints: Call number 3 */
    return 0;
}

At file scope, static changes linkage from external to internal. The variable keeps file scope and static storage duration, but it is no longer accessible by name from any other translation unit. File-level static is the C equivalent of a private global: visible to all functions in the same file, invisible outside it.

static int filePrivate = 0;   /* file scope, internal linkage */

extern

extern declares that a variable is defined elsewhere. It does not allocate storage on its own. It creates a reference to an existing definition in another translation unit or later in the same file. A common mistake is writing extern int x = 5; with an initializer, which is a definition in C (not just a declaration) and causes a linker collision if another translation unit also defines x.

register

register was originally a hint to the compiler to store the variable in a CPU register for faster access. Modern compilers ignore the hint entirely. The one constraint that still matters in practice: you cannot apply the & operator to a register variable. Attempting to do so is a compile error per C11. This rule appears regularly as a distractor in placement MCQs, because students who know that register is “just a hint” may assume the address restriction no longer applies.

void example(void) {
    register int i = 0;
    /* int *p = &i;  -- compile error: cannot take address of register variable */
    (void)i;
}

Output Puzzles: What Placement Tests Actually Ask

Most C scope questions in TCS NQT and Infosys aptitude rounds take this form: a short program, a printf, and four answer choices that diverge because of scope rules. The traps are consistent.

Shadowing

#include <stdio.h>

int x = 100;   /* file scope */

int main(void) {
    int x = 200;   /* block scope — shadows the global */
    printf("%d\n", x);
    return 0;
}
  • What the test expects: 200. The local x shadows the global x inside main. Every reference to x within that block resolves to the local copy.
  • Common wrong answer: 100. Candidates who read the global declaration first and don’t track the local declaration choose this.

The global is not modified. After main returns, the global x still holds 100.

Static Counter Across Calls

#include <stdio.h>

int next(void) {
    static int n = 0;
    n++;
    return n;
}

int main(void) {
    int a = next();
    int b = next();
    int c = next();
    printf("%d %d %d\n", a, b, c);   /* prints: 1 2 3 */
    return 0;
}

This version calls next() in separate statements to avoid the evaluation-order problem. Some placement MCQs present all three calls inside a single printf argument list. The C standard does not guarantee left-to-right evaluation of function arguments, so that version’s output is implementation-defined. Reading the question carefully (separate statements vs. one expression) is part of what the test measures.

Practice questions on C scope, shadowing, and output tracing are collected in C coding questions set 1 alongside aptitude and logical-reasoning drills.

Scope Bugs That Cost Candidates Marks in Practical Rounds

Several universities and companies add a write-and-compile stage after the MCQ round. Scope bugs that compile without error but produce wrong output are the most dangerous category.

Returning a pointer to a block-scoped variable:

int *danger(void) {
    int local = 42;
    return &local;   /* undefined behavior: local is freed when the function returns */
}

The compiler may warn with -Wall in GCC, but the program compiles. The returned pointer points to freed stack memory. Dereferencing it is undefined behavior. Candidates who test for “does it compile?” rather than “is the behavior defined?” miss this class of bug.

Assuming an uninitialized static variable holds garbage:

Uninitialized static variables are zero-initialized by the C runtime per C11 §6.7.9. This is defined behavior, not an undefined one. Candidates who apply the rule for auto variables (uninitialized auto holds garbage) to static variables will trace the wrong output when the initial value matters.

Using extern without a matching definition:

Declaring extern int counter; in a file without a corresponding definition anywhere in the linked program causes a linker error, not a compile error. Students who compile only one file at a time will miss this. The error appears only when the linker tries to resolve the symbol across the full program.

Working through Pascal’s triangle in C is a practical exercise that applies nested block scope to loop variables and multi-dimensional arrays. See Pascal’s triangle in C for the step-by-step implementation.

Quick Reference: Scope vs. Storage Duration vs. Linkage

SpecifierWhere declaredScopeStorage durationLinkage
(none)Inside function/blockBlockAutomaticNone
autoInside function/blockBlockAutomaticNone
staticInside function/blockBlockStaticNone
staticOutside all functionsFileStaticInternal
(none)Outside all functionsFileStaticExternal
externAny locationFileStaticExternal
registerInside function/blockBlockAutomaticNone

The auto and register rows are identical in scope, storage duration, and linkage. The only practical distinction: register prohibits taking the variable’s address.

Practical Takeaway

Scope rules in C are defined precisely in the standard and do not change between compiler versions. The four scope kinds are fixed. The four storage classes interact with them in predictable ways. A candidate who has traced output through a dozen shadowing and static counter examples before the test will not be surprised by a placement MCQ that combines a global, a local shadow, and a static counter in the same ten-line snippet.

The gap between tracing scope on paper and doing it under time pressure is practice volume. TinkerLLM has a code environment where you can paste and run every example in this article, including the shadowing trap, the static counter, and the register address error, without installing a local C compiler. At ₹299, it’s the fastest way to close the distance between knowing the four scope kinds and trusting your trace under test conditions.

Primary sources

Frequently asked questions

What is the difference between block scope and function scope in C?

Block scope applies to variables declared inside any pair of braces. Function scope in C applies only to labels, which are the targets of goto statements. A variable declared at the top of a function body has block scope, not function scope.

Does a static variable inside a function retain its value between calls?

Yes. A static variable inside a function has static storage duration: it is initialized once (to zero if not explicitly initialized) and retains its value across every subsequent call to that function.

Can you take the address of a register variable in C?

No. The C11 standard explicitly forbids applying the & operator to a register-qualified variable. Most modern compilers treat register as a no-op hint, but the address restriction still applies syntactically.

What is file scope in C?

A variable declared outside all functions has file scope. It is visible from its point of declaration to the end of the translation unit. Adding the static keyword to a file-scope variable restricts its linkage to that translation unit only.

What happens when a local variable has the same name as a global in C?

The local variable shadows the global within its block. The global is still accessible in other scopes; inside the block, any reference to that name reaches the local copy. The global value is unchanged by assignments to the local.

What is function prototype scope in C?

Parameter names in a function declaration (prototype) have function prototype scope: they exist only within the prototype and can differ from the names used in the actual function definition.

Build AI projects

A self-paced playground for building with LLMs.

TinkerLLM is FACE Prep's sister property. A guided environment for shipping real LLM applications, the kind of project that earns a paragraph on your resume, not a line.

Try TinkerLLM (₹299 launch)
Free AI Roadmap PDF