100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C

Segmentation

Dividing a program's address space into variable-sized, logically meaningful segments such as code, stack, and heap.

Memory ManagementIntermediate9 min readJul 8, 2026
Analogies

Introduction

Segmentation is a memory management scheme that supports the programmer's logical view of a program: rather than one flat, arbitrarily numbered stream of bytes, a program is seen as a collection of variable-length, meaningful segments -- for example, main program code, a library function, the stack, the heap, and a symbol table. Each segment has a name (in practice, a segment number) and a length that is determined by its actual content, unlike pages, which are all fixed and equal in size regardless of what they hold.

🏏

Cricket analogy: A team's tour dossier isn't one flat file but distinct meaningful sections -- the batting lineup, the bowling attack, the fielding plan, the injury list -- each sized to however much content it actually needs, unlike a fixed-size scorecard page that's the same length regardless of what's on it.

Explanation

A logical address under segmentation is a pair: (segment number s, offset d). The OS maintains a segment table per process, where each entry stores a base (the starting physical address of that segment) and a limit (the segment's length). To translate an address, the hardware indexes the segment table with s, checks that 0 <= d < limit (raising a protection fault if not), and computes physical_address = base + d. The key contrast with paging is size and meaning: pages are fixed-size, hardware-defined chunks with no relationship to program structure, while segments are variable-size, logically meaningful units chosen by the compiler or programmer -- this makes segmentation a natural fit for sharing (e.g., sharing a read-only code segment among processes) and for applying different protection bits (read/write/execute) per segment, since an entire segment such as code can be marked read-execute-only while the stack is read-write. Because segments vary in size, segmentation reintroduces external fragmentation, the very problem paging was designed to avoid; many real systems therefore combine the two as segmentation with paging, where each segment is itself paged internally.

🏏

Cricket analogy: A player's stats are looked up by (innings number, ball number): the scorer indexes the innings table, checks the ball number is within that innings's actual ball count (raising a foul if not), and computes the exact delivery -- unlike pages, an innings varies in length depending on how the match actually unfolds, making it natural to share a bowling-figures section across formats or restrict who can edit the declared-innings record, though variable innings lengths can create awkward gaps in the schedule, which is why some leagues combine fixed-over blocks within variable innings.

Example

c
#include <stdio.h>

typedef struct {
    unsigned int base;
    unsigned int limit;
} SegmentTableEntry;

int translate(int segment, unsigned int offset, SegmentTableEntry *seg_table, unsigned int *phys_out) {
    SegmentTableEntry entry = seg_table[segment];
    if (offset >= entry.limit) {
        printf("Protection fault: offset %u exceeds limit %u for segment %d\n",
               offset, entry.limit, segment);
        return -1;
    }
    *phys_out = entry.base + offset;
    return 0;
}

int main(void) {
    /* Segment 0 = code, Segment 1 = stack, Segment 2 = heap */
    SegmentTableEntry seg_table[3] = {
        {4300, 1200},   /* code:  base 4300, length 1200 bytes */
        {6500, 400},    /* stack: base 6500, length 400 bytes  */
        {8000, 2500}    /* heap:  base 8000, length 2500 bytes */
    };

    unsigned int physical;
    if (translate(1, 350, seg_table, &physical) == 0)
        printf("Segment 1, offset 350 -> physical address %u\n", physical);

    translate(1, 450, seg_table, &physical);  /* exceeds stack limit of 400 */
    return 0;
}

Output

The first call translates (segment 1, offset 350). Since 350 < limit (400), it is valid; physical address = base(6500) + offset(350) = 6850. The second call, (segment 1, offset 450), fails because 450 >= 400, so the hardware raises a protection fault before any memory is touched -- this is exactly the kind of out-of-bounds access (e.g., a stack overflow) that segment limits are designed to catch, something a flat single-base/limit contiguous scheme could only do for the whole process, not per logical unit.

🏏

Cricket analogy: Checking the 350th ball bowled against an innings limit of 400 balls, it's valid, so the delivery is logged at the correct spot in the innings record; but checking the 450th ball against that same 400-ball limit fails immediately -- the scorer flags an error before recording anything, exactly the kind of over-the-limit mistake a single flat match tally could never catch per innings.

Key Takeaways

  • A segmented logical address is (segment number, offset); a segment table stores base and limit per segment.
  • Physical address = segment_table[s].base + d, only valid if 0 <= d < segment_table[s].limit.
  • Segments are variable-size and logically meaningful (code, stack, heap); pages are fixed-size and have no relation to program structure.
  • Segmentation naturally supports per-segment protection (e.g., read-execute code, read-write data) and sharing of common segments across processes.
  • Because segments vary in size, segmentation can suffer external fragmentation; many systems use paged segmentation to combine paging's fragmentation-free allocation with segmentation's logical structure.

Practice what you learned

Was this page helpful?

Topics covered

#OperatingSystemsStudyNotes#OperatingSystems#Segmentation#Explanation#Example#Output#Key#StudyNotes#SkillVeris#ExamPrep