C memory layout: text, data, heap, stack
The six regions of a running C program's address space, which kind of variable lives in which region, and why that decides lifetime, initial value and whether you may write to it.
Why it exists
A running program is not just "variables in memory". The operating system gives it one address space, and the compiler and linker sort every object in the program into regions with different rules (the loader then maps them into memory). Where something lives decides how long it exists, what value it starts with, whether you can change it, and how fast it can grow. Questions of the form "which of these is stored where", and many pointer-bug questions, come from this one picture.
Prerequisites
Precise definitions
A C program's address space has six regions that matter. From the lowest addresses of the program image upward:
| Region | What lives there | Lifetime | Initial value | Writable |
|---|---|---|---|---|
| Text | The machine code of every function | whole program | the compiled instructions | no (read-only, executable) |
| Read-only data | String literals, const globals | whole program | the stored constants | no |
| Data | Globals and statics with a non-zero initialiser | whole program | stored in the executable file | yes |
| BSS | Globals and statics without an initialiser (gcc also puts ones initialised to zero here) | whole program | zero, filled in by the loader | yes |
| Heap | Memory from malloc / calloc / realloc | until free | uninitialised (calloc gives zeros) | yes |
| Stack | Local variables, parameters, return addresses (conceptually; a compiler may keep some in registers) | until the function returns | uninitialised (indeterminate) | yes |
This table describes the usual gcc and Linux layout. The C standard itself defines only storage durations and says that modifying a string literal or a const-defined object is undefined; it does not say which memory region holds them.
Definitions to know exactly:
- Storage duration (C11 6.2.4). Static: exists for the whole run (globals,
staticlocals, string literals). Automatic: exists from entering the block to leaving it (ordinary locals and parameters). Allocated: exists frommalloc(orcalloc,realloc) tofree. C11 also has thread storage duration (_Thread_local), which is not needed for GATE. - Stack frame. The block of the stack belonging to one function call (see recursion and the call stack).
- Text, data and BSS are the three columns printed by the
sizecommand. Its text column covers the code and the read-only data; data is initialised writable data; bss is zero-initialised data. Everything except BSS takes space in the executable file; BSS is recorded as a size only. (The file actually has more than three sections, such as.rodata;sizejust groups them.) - Initialisation rule. Objects with static storage duration that you do not initialise are set to zero. Automatic objects you do not initialise have an indeterminate value; reading one is a bug.
Growth directions. On the usual x86 layout the stack grows toward lower addresses (each new call's frame sits below the caller's) and the classic heap (the one extended with brk) grows toward higher addresses; large malloc requests may instead be served from separate mappings elsewhere. This is a property of the platform, not of C. GATE questions that mention a direction state it.
How it works
Step through a real program. Each step places one object: you see its declaration, the region it landed in, and its real address. The addresses come from a run with address randomisation turned off so they are stable. With randomisation on, the absolute numbers change on every run, but the usual order of the regions and which object goes where do not (the gaps between regions do change).
Process memory map
Real addresses from a runC source
Address spacehigh addresses ↑
⋮ shared libraries and other mappings ⋮
the executable file's own regions
Step 0
A running C program has one address space, split into regions. Where a variable or function lives decides how long it lasts, whether it can be changed and what its initial value is. Step through the program to see where each object really lands.
With ASLR on, absolute addresses change on every run, but the order of the regions and which object lands in which region does not.
What to take from it:
- Only the pointer is on the stack.
pis a local, sopitself is on the stack, but the four integers it points to were obtained frommallocand are on the heap.nameis likewise a stack variable holding the address of characters that sit in read-only memory. static int callsis not on the stack even though it is declared insidemain. Its initial value 3 is stored in the file, so it is in the data region, next tototal.- Uninitialised globals cost nothing in the file.
countandhitshave no initialiser, so they are in BSS. Thesizeline at the end shows the real split for this program. - Code has addresses too.
mainis a function, and it has an address in the text region. A function name used in an expression is converted to a pointer to that code.
Here is a fact the animation cannot show, but a real run can: writing to a string literal.
The same program, run for real
Real runs on this machineWriting into a string literal
#include <stdio.h>
int main(void) {
char *s = "GATE";
s[0] = 'g';
printf("%s\n", s);
return 0;
}What the compiled program did
(no output)gcc -O0
(no output)gcc -O2
The GATE answer
Undefined behaviour. A string literal may be stored in read-only memory, and writing to it is not allowed. On typical systems the program is killed by SIGSEGV. A char array initialised from the literal is a separate writable copy. Here every setting happened to agree, which proves nothing.
Worked examples
EX 1Sort the variables into regions
EasyFor the program in the animation, the regions are:
| Object | Region | Why |
|---|---|---|
main | text | machine code |
"GATE" | read-only data | string literal |
limit | read-only data | const global |
total | data | global with value 10 |
calls | data | static local with value 3 |
count | BSS | global, no initialiser |
hits | BSS | file-static, no initialiser |
*p (the malloc block) | heap | allocated |
x, arr, p | stack | automatic locals |
These regions are exactly what the real program reported (the generator stops with an error if any object lands elsewhere).
EX 2A static local remembers; an automatic local does not
Medium#include <stdio.h>
int next(void) {
static int n = 0;
int fresh = 0;
n++;
fresh++;
return n * 10 + fresh;
}
int main(void) {
int first = next();
int second = next();
int third = next();
printf("%d %d %d\n", first, second, third);
return 0;
}Output:
11 21 31Step 1. n is static, so it lives in the data region and keeps its value between calls. It becomes 1, then 2, then 3.
Step 2. fresh is automatic, so it is created at 0 on every call and ends each call at 1.
Step 3. The returned values are , and .
Why the calls are in separate statements. If you wrote printf("%d %d %d", next(), next(), next()) the order in which the three calls run is unspecified, so the printed order could differ between compilers. The page on undefined behaviour shows this with real runs.
EX 3Default values of globals and locals
Medium#include <stdio.h>
int g[3];
static int s;
int main(void) {
printf("%d %d %d %d\n", g[0], g[1], g[2], s);
return 0;
}Output:
0 0 0 0Why. Both objects have static storage duration and no initialiser, so they are in BSS and start as zero. An uninitialised local int x; has an indeterminate value; no output can be predicted for it, so a correct GATE question never asks.
EX 4A pointer to a literal against an array copy
GATE level#include <stdio.h>
int main(void) {
char *s = "GATE";
char t[] = "GATE";
t[0] = 'g';
printf("%zu %zu\n", sizeof s, sizeof t);
printf("%s %s\n", s, t);
return 0;
}Output:
8 5
GATE gATEStep 1. s is a pointer variable. Its size is the pointer size, 8 bytes on this 64-bit machine (a GATE question will state the pointer size, commonly 4 or 8). The characters it points to are in read-only memory and must not be modified.
Step 2. t is an array of 5 char (four letters plus the terminating '\0'), initialised by copying the literal. The array is an ordinary local on the stack, so sizeof t is 5 and t[0] = 'g' is legal.
Step 3. Writing s[0] = 'g' instead would be undefined and normally crashes (see the real run above).
EX 5Returning the address of something
GATE level#include <stdio.h>
#include <stdlib.h>
int *from_static(void) {
static int v = 5;
return &v;
}
int *from_heap(void) {
int *p = malloc(sizeof *p);
*p = 6;
return p;
}
int main(void) {
int *a = from_static();
int *b = from_heap();
printf("%d %d\n", *a, *b);
free(b);
return 0;
}Output:
5 6Both are safe because the pointed-to object outlives the function: the static lives in the data region for the whole run, and the heap block lives until free.
The broken version returns &x where x is an ordinary local. When the function returns, its stack frame is gone, so the returned address points to memory that will soon be reused (a dangling pointer). Using it is undefined behaviour.
Cheat box
Cheat box
- Regions: text (code, read-only) · rodata (literals,
constglobals) · data (initialised globals/statics) · BSS (uninitialised globals/statics, zero-filled) · heap (malloc) · stack (locals, frames). - Static storage (globals,
static, literals): lives for the whole run; if uninitialised it is zero. Automatic (locals): lives until the block ends; if uninitialised it is indeterminate. - BSS takes no room in the executable file. Data does.
- Stack typically grows down, heap up. Frame is popped on return.
- A pointer variable and the thing it points to can be in different regions.
char *s = "x"points to read-only memory.char s[] = "x"is a writable copy.sizeprints text, data and bss sizes of the executable.
Traps and GATE patterns
(needs verification: whether GATE treats a const global as read-only data or just as "data". gcc places it in read-only data, as the animation shows, but the C standard does not mandate a region, so a question that simplifies to "initialised data" may expect data.)
Connections
Unlocks
Across subjects
Within this subject
Journeys that pass through here
The same six regions are what the operating system builds for every process; see the process address space. A compiler thinks of this as storage organisation, and each stack frame is an activation record.
PYQs
3 previous year questions from your book. I re-solved each one myself (1 by running or simulating, 2 by reasoning only); the book's key is shown next to my answer. All PYQs
In C runtime environment, which one of the following is stored in heap?
- (a)A static variable declared inside a function
- (b)An array of integers declared inside a function
- (c)A dynamically allocated array of integers created using malloc() function call
- (d)Return address of a function
Match the following: (P) static char var ; (i) Sequence of memory locations to store address (Q) m= malloc (10); m = NULL; (ii) A variable located in data section of memory (R) char*ptr[10]; (iii) Request to allocate a CPU register to store data (S) register int var1; (iv) A lost memory which cannot be freed
- (a)P→ (ii), Q→ (iv), R→ (i), S→ (iii)
- (b)P→ (ii), Q→ (i), R→ (iv), S→ (iii)
- (c)P→ (ii), Q→ (iv), R→ (iii), S→ (i)
- (d)P→ (iii), Q→ (iv), R→ (i), S→ (ii)
If the numerical value of a 2-byte unsigned integer on a little-endian computer is 255 more than that on a big-endian computer, which of the following choices represent(s) the unsigned integer on a little-endian computer?
- (a)0x6665
- (b)0x0001
- (c)0x4243
- (d)0x0100
Self-check
Question 1
Where is each of these stored: a global int a[1000000]; with no initialiser, and the same array declared inside main? Which one makes the executable file bigger?
Show answerHide
The global is in BSS (static storage, no initialiser); the one in main is on the stack. Neither makes the file large: BSS is only a size in the file, and a stack array exists only at run time. A global with initialisers (int a[1000000] = {1};) would be in the data region and would add about 4 MB to the file. On a typical gcc and Linux build the global array above gives a file of about 16 KB with 4,000,032 bytes of BSS.
Question 2
Why can a local int big[10000000]; crash main while the same array as a global works?
Show answerHide
The local array is placed on the stack, whose size is limited (typically 8 MB on Linux). 40 MB does not fit, so the program overflows the stack. The global lives in BSS, which is not limited by the stack size.
Question 3
What does this print?
#include <stdio.h>
void f(void) {
static int s = 0;
int a = 0;
s++;
a++;
printf("%d %d\n", s, a);
}
int main(void) {
f();
f();
return 0;
}Show answerHide
s is static and survives; a is recreated each call.
1 1
2 1Question 4
After char *s = "hello"; char t[] = "hello";, which of s[0] = 'j'; and t[0] = 'j'; is legal, and why?
Show answerHide
Only t[0] = 'j';. t is a writable stack array that holds a copy of the characters. s points at the string literal in read-only memory, so writing through it is undefined behaviour (usually a crash).
Revision card
One-screen revision
- Six regions: text, rodata, data, BSS, heap, stack.
- Static storage = whole run, default zero (or null). Automatic = block lifetime, indeterminate unless initialised. Heap = until
free. - Initialised globals/statics → data. Uninitialised → BSS. Literals and
constglobals → read-only. - Locals, parameters, return addresses → stack (grows down typically).
malloc→ heap (grows up). pthe pointer (stack) is separate from*p(heap).char *s = "x": read-only.char s[] = "x": writable copy.- Never return the address of an automatic variable.
References
- ·Bryant and O'Hallaron, Computer Systems: A Programmer's Perspective, 3rd ed., Section 7.4 Relocatable Object Files (.text, .rodata, .data, .bss) and Section 9.8 Memory Mapping
- ·Aho, Lam, Sethi, Ullman, Compilers: Principles, Techniques, and Tools, 2nd ed., Section 7.1 Storage Organization
- ·ISO/IEC 9899:2011 (C11), Section 6.2.4 Storage durations of objects
Verification
Every address, region and size in the animation is printed by a real gcc-compiled program run with ASLR off; the generator fails if any object lands in a region other than the one stated here. All C programs on this page are compiled and run by npm run verify:c. The prose and 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
My notes (saved in this browser)