1. What is the purpose of the volatile keyword in C variable declarations?
Answer: It tells the compiler the variable can change outside the program’s control, forcing a fresh memory read/write on every access instead of caching.
volatile prevents the compiler from assuming it knows the variable’s value. Without it, the compiler may keep a copy in a register or optimize away repeated reads (since “nothing else could change it”). With volatile, every access goes to the actual memory location.
Why it matters — the value genuinely changes from the program’s perspective:
- Memory-mapped hardware registers (device I/O).
- Variables modified by signal handlers.
- Shared state updated by another thread or by interrupt-driven code.
volatile uint8_t *status = (volatile uint8_t *)0x4000; // hardware register
while (*status & BUSY) { /* poll */ } // must re-read every iteration
Note: volatile is about reads and writes not being optimized away — it is not about atomicity or thread-safety. For concurrent threads you still need synchronization. The interview answer: volatile forces direct memory access on every read/write, preventing the compiler from caching or eliding accesses to values that change externally.
Answer:
It tells the compiler the variable can change outside the program’s control, forcing a fresh memory read/write on every access instead of caching.
volatile prevents the compiler from assuming it knows the variable’s value. Without it, the compiler may keep a copy in a register or optimize away repeated reads (since “nothing else could change it”). With volatile, every access goes to the actual memory location.
Why it matters — the value genuinely changes from the program’s perspective:
- Memory-mapped hardware registers (device I/O).
- Variables modified by signal handlers.
- Shared state updated by another thread or by interrupt-driven code.
volatile uint8_t *status = (volatile uint8_t *)0x4000; // hardware register
while (*status & BUSY) { /* poll */ } // must re-read every iteration
Note: volatile is about reads and writes not being optimized away — it is not about atomicity or thread-safety. For concurrent threads you still need synchronization. The interview answer: volatile forces direct memory access on every read/write, preventing the compiler from caching or eliding accesses to values that change externally.
2. What does the static keyword mean when applied to a global variable declared outside any function?
Answer: It gives the variable internal linkage — its scope is restricted to the translation unit (source file) where it’s declared.
At file scope, static changes linkage, not storage (a file-scope variable is already static-storage-duration). With static, the variable’s name is only visible within its own translation unit; it won’t collide with same-named symbols in other source files at link time.
// in file1.c
static int counter = 0; // only file1.c sees this
- Without
static:externlinkage — the name is visible program-wide (thoughconstglobals have internal linkage by default in C++ but not C). - With
static: internal linkage — private to the file. This is the standard way to make file-local state and helper globals that shouldn’t leak into the global namespace.
Note this is different from static inside a function, which means “initialized once, persists between calls.” The interview answer: at global scope, static restricts the variable’s linkage to its own translation unit (file), preventing external symbol collisions.
Answer:
It gives the variable internal linkage — its scope is restricted to the translation unit (source file) where it’s declared.
At file scope, static changes linkage, not storage (a file-scope variable is already static-storage-duration). With static, the variable’s name is only visible within its own translation unit; it won’t collide with same-named symbols in other source files at link time.
// in file1.c
static int counter = 0; // only file1.c sees this
- Without
static:externlinkage — the name is visible program-wide (thoughconstglobals have internal linkage by default in C++ but not C). - With
static: internal linkage — private to the file. This is the standard way to make file-local state and helper globals that shouldn’t leak into the global namespace.
Note this is different from static inside a function, which means “initialized once, persists between calls.” The interview answer: at global scope, static restricts the variable’s linkage to its own translation unit (file), preventing external symbol collisions.
3. What is the scope and lifetime of a local variable declared with static inside a function body?
Answer: Block scope (only the function can see it), but lifetime spans the whole program — it keeps its value between calls and is initialized once.
A local static variable combines two properties:
- Scope: block/function scope — accessible only inside the function where it’s declared (same visibility rules as a normal local).
- Lifetime: static storage duration — it lives for the entire program run, not just the function call. It’s initialized once (before the program starts), and retains its value across calls.
int next() {
static int counter = 0; // initialized once
return counter++;
}
Each call to next() returns an increasing value — the counter survives between calls. That’s the classic use: persistent state without a global. The interview answer: block scope, program-long lifetime, initialized once, value preserved between calls.
Answer:
Block scope (only the function can see it), but lifetime spans the whole program — it keeps its value between calls and is initialized once.
A local static variable combines two properties:
- Scope: block/function scope — accessible only inside the function where it’s declared (same visibility rules as a normal local).
- Lifetime: static storage duration — it lives for the entire program run, not just the function call. It’s initialized once (before the program starts), and retains its value across calls.
int next() {
static int counter = 0; // initialized once
return counter++;
}
Each call to next() returns an increasing value — the counter survives between calls. That’s the classic use: persistent state without a global. The interview answer: block scope, program-long lifetime, initialized once, value preserved between calls.
4. What is the value of an uninitialized local automatic variable inside a function body?
Answer: Indeterminate (garbage) — whatever bit pattern happens to be in that stack memory; reading it before writing is undefined behavior.
Local (automatic) variables are not zero-initialized. They contain whatever leftover data occupied that stack location — stale values from previous frames. The exact content is unpredictable.
void f() {
int x; // indeterminate value
printf("%d", x); // UB: reading an uninitialized variable
}
Reading an uninitialized automatic variable is undefined behavior in C (for non-character types; even char uninitialized reads are UB before assignment in practice, though unsigned char/char are special-cased for indeterminate reads). The value is never guaranteed to be 0, NULL, or anything specific.
Contrast: static and global variables are zero-initialized. The interview answer: indeterminate garbage from the stack; reading before assignment is undefined behavior.
Answer:
Indeterminate (garbage) — whatever bit pattern happens to be in that stack memory; reading it before writing is undefined behavior.
Local (automatic) variables are not zero-initialized. They contain whatever leftover data occupied that stack location — stale values from previous frames. The exact content is unpredictable.
void f() {
int x; // indeterminate value
printf("%d", x); // UB: reading an uninitialized variable
}
Reading an uninitialized automatic variable is undefined behavior in C (for non-character types; even char uninitialized reads are UB before assignment in practice, though unsigned char/char are special-cased for indeterminate reads). The value is never guaranteed to be 0, NULL, or anything specific.
Contrast: static and global variables are zero-initialized. The interview answer: indeterminate garbage from the stack; reading before assignment is undefined behavior.
5. What is the memory location where uninitialized global and static variables are stored?
Answer: The BSS segment (Block Started by Symbol).
A program’s memory layout separates data by initialization state:
- Text/Code — executable instructions.
- Data segment — initialized globals/statics.
- BSS — uninitialized globals and statics. The OS zeroes BSS before program start, so uninitialized globals reliably read as
0. - Heap — dynamic allocation (grows toward higher addresses).
- Stack — function call frames (grows toward lower addresses).
int g; // BSS — zeroed before main
static int s; // BSS — zeroed before main
That’s why globals/statics are guaranteed 0 while automatic (local) variables are garbage. The interview answer: uninitialized globals/statics live in BSS, which the OS zeroes before execution starts.
Answer:
The BSS segment (Block Started by Symbol).
A program’s memory layout separates data by initialization state:
- Text/Code — executable instructions.
- Data segment — initialized globals/statics.
- BSS — uninitialized globals and statics. The OS zeroes BSS before program start, so uninitialized globals reliably read as
0. - Heap — dynamic allocation (grows toward higher addresses).
- Stack — function call frames (grows toward lower addresses).
int g; // BSS — zeroed before main
static int s; // BSS — zeroed before main
That’s why globals/statics are guaranteed 0 while automatic (local) variables are garbage. The interview answer: uninitialized globals/statics live in BSS, which the OS zeroes before execution starts.
6. What does the expression (void)var; do in C source files?
Answer: It suppresses “unused variable” compiler warnings intentionally, without emitting any runtime code.
Writing (void)var; as a statement casts var to void and discards it. The compiler sees the variable as “used” (it appears in an expression), so it won’t warn about it being unused — but casting to void means “I’m deliberately ignoring this,” and no machine code is generated for it.
Typical uses:
- Function parameters that are intentionally unused (e.g., callback signatures where you don’t need every argument).
- Suppressing warnings in macro-generated code.
- Documenting that a variable is intentionally ignored.
It’s purely a compile-time signal to the compiler and a readability note to humans. The interview answer: an intentional no-op that silences unused-variable warnings without runtime cost.
Answer:
It suppresses “unused variable” compiler warnings intentionally, without emitting any runtime code.
Writing (void)var; as a statement casts var to void and discards it. The compiler sees the variable as “used” (it appears in an expression), so it won’t warn about it being unused — but casting to void means “I’m deliberately ignoring this,” and no machine code is generated for it.
Typical uses:
- Function parameters that are intentionally unused (e.g., callback signatures where you don’t need every argument).
- Suppressing warnings in macro-generated code.
- Documenting that a variable is intentionally ignored.
It’s purely a compile-time signal to the compiler and a readability note to humans. The interview answer: an intentional no-op that silences unused-variable warnings without runtime cost.
7. What is the default access modifier of global variables in C across multiple translation units if declared without static?
Answer: External linkage — the variable is visible program-wide, and other translation units can access it with extern.
A file-scope variable declared without static has external linkage by default:
// file1.c
int counter = 0; // external linkage
// file2.c
extern int counter; // refers to file1.c's counter
counter++;
That’s what makes a name usable across translation units. Adding static flips it to internal linkage, hiding the variable from other files. (Contrast C++: a const file-scope variable has internal linkage by default in C++, but not in C — in C, const globals are still external by default unless marked static.) The interview answer: external linkage by default; other translation units reach it via extern.
Answer:
External linkage — the variable is visible program-wide, and other translation units can access it with extern.
A file-scope variable declared without static has external linkage by default:
// file1.c
int counter = 0; // external linkage
// file2.c
extern int counter; // refers to file1.c's counter
counter++;
That’s what makes a name usable across translation units. Adding static flips it to internal linkage, hiding the variable from other files. (Contrast C++: a const file-scope variable has internal linkage by default in C++, but not in C — in C, const globals are still external by default unless marked static.) The interview answer: external linkage by default; other translation units reach it via extern.
8. What happens when compiling an undefined variable reference in C without declaring it extern or local?
Answer: A compile error — “Undeclared identifier” — or a link error (“unresolved external symbol”) if the declaration exists but the definition doesn’t.
C requires every identifier to be declared before use. If you reference a variable that was never declared:
- Compile time: “use of undeclared identifier” (or implicit-declaration error).
If you declare it (extern int counter;) but never define it anywhere (no int counter; in any translation unit), then the linker fails: “unresolved external symbol.”
extern int counter; // declared but never defined anywhere
counter = 1; // link error: unresolved external
So: missing declaration → compiler error; declaration without definition → linker error. The interview answer: a compile error (undeclared identifier) or a link error (unresolved external symbol) for a declared-but-never-defined variable.
Answer:
A compile error — “Undeclared identifier” — or a link error (“unresolved external symbol”) if the declaration exists but the definition doesn’t.
C requires every identifier to be declared before use. If you reference a variable that was never declared:
- Compile time: “use of undeclared identifier” (or implicit-declaration error).
If you declare it (extern int counter;) but never define it anywhere (no int counter; in any translation unit), then the linker fails: “unresolved external symbol.”
extern int counter; // declared but never defined anywhere
counter = 1; // link error: unresolved external
So: missing declaration → compiler error; declaration without definition → linker error. The interview answer: a compile error (undeclared identifier) or a link error (unresolved external symbol) for a declared-but-never-defined variable.
9. What does the storage class register suggest to the compiler?
Answer: A hint that the variable is heavily accessed and should be kept in a CPU register — though modern compilers generally ignore it.
register tells the compiler “this variable is used often; put it in a register if you can.” In modern optimizing compilers, register allocation is fully automatic, so register is essentially ignored as a hint. Remaining semantic restrictions:
- You cannot take the address (
&var) of aregistervariable (since it may have no memory address). - It applies only to block-scope (and function-parameter) variables, not globals.
The keyword survives mostly for historical/portability reasons and is now largely vestigial in practice. The interview answer: a mostly-ignored hint that the variable should live in a register; taking its address is illegal.
Answer:
A hint that the variable is heavily accessed and should be kept in a CPU register — though modern compilers generally ignore it.
register tells the compiler “this variable is used often; put it in a register if you can.” In modern optimizing compilers, register allocation is fully automatic, so register is essentially ignored as a hint. Remaining semantic restrictions:
- You cannot take the address (
&var) of aregistervariable (since it may have no memory address). - It applies only to block-scope (and function-parameter) variables, not globals.
The keyword survives mostly for historical/portability reasons and is now largely vestigial in practice. The interview answer: a mostly-ignored hint that the variable should live in a register; taking its address is illegal.
10. What is the primary operational issue with static variables declared inside functions in multithreaded C programs?
Answer: A function-local static variable is a single shared instance across all threads — concurrent access with a write causes data races unless synchronized.
Despite being declared inside a function, a static local lives in the program’s data segment, shared by every thread that calls the function. If two threads call the function and both read/write that variable without synchronization, that’s a data race (undefined behavior in C11’s memory model) — corrupted or inconsistent values.
int next() {
static int counter = 0; // ONE copy for all threads
return counter++; // race if called concurrently
}
Fixes: protect with a mutex, use C11 atomics (_Atomic), or make it thread-local (C11 _Thread_local/thread_local) if each thread should have its own instance. The interview answer: the static is shared by all threads, causing data races on concurrent access unless mutex/atomic/thread-local is used.
Answer:
A function-local static variable is a single shared instance across all threads — concurrent access with a write causes data races unless synchronized.
Despite being declared inside a function, a static local lives in the program’s data segment, shared by every thread that calls the function. If two threads call the function and both read/write that variable without synchronization, that’s a data race (undefined behavior in C11’s memory model) — corrupted or inconsistent values.
int next() {
static int counter = 0; // ONE copy for all threads
return counter++; // race if called concurrently
}
Fixes: protect with a mutex, use C11 atomics (_Atomic), or make it thread-local (C11 _Thread_local/thread_local) if each thread should have its own instance. The interview answer: the static is shared by all threads, causing data races on concurrent access unless mutex/atomic/thread-local is used.
11. What does the storage class extern signify when applied to a variable declaration inside a function frame (extern int count;)?
Answer: It declares that count refers to a global variable defined elsewhere — it does not allocate new storage.
extern is a declaration without definition: it tells the compiler “this name refers to storage that exists elsewhere (another file, or a global scope) — don’t allocate memory for it here.” Inside a function, extern int count; lets you reference a file-scope global by name without creating a local copy:
int count; // global definition in file1.c
// file2.c
void f() {
extern int count; // refers to file1.c's global, no new memory
count++;
}
Contrast a plain local int count; — that allocates a new automatic variable shadowing the global. extern in block scope simply links the name to the external definition, and prevents a separate local allocation. The interview answer: extern references storage defined elsewhere (a global in another scope/file) without allocating new memory.
Answer:
It declares that count refers to a global variable defined elsewhere — it does not allocate new storage.
extern is a declaration without definition: it tells the compiler “this name refers to storage that exists elsewhere (another file, or a global scope) — don’t allocate memory for it here.” Inside a function, extern int count; lets you reference a file-scope global by name without creating a local copy:
int count; // global definition in file1.c
// file2.c
void f() {
extern int count; // refers to file1.c's global, no new memory
count++;
}
Contrast a plain local int count; — that allocates a new automatic variable shadowing the global. extern in block scope simply links the name to the external definition, and prevents a separate local allocation. The interview answer: extern references storage defined elsewhere (a global in another scope/file) without allocating new memory.
Premium Content
Unlock Storage Classes & Scope and all premium lessons with a subscription.
From ₹199.99/year — See plans