Project 1: Memory Visualizer
Build a tool that prints addresses, stack frames, heap allocations, and raw bytes to make C memory tangible.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Beginner |
| Time Estimate | Weekend |
| Language | C (Alternatives: Rust) |
| Prerequisites | Basic C syntax, printf |
| Key Topics | Memory layout, pointers, stack/heap |
1. Learning Objectives
By completing this project, you will:
- Visualize stack and heap addresses at runtime.
- Explain how variables map to memory.
- Print raw bytes to understand endianness.
- Use a debugger to validate your mental model.
2. Theoretical Foundation
2.1 Core Concepts
- Virtual memory layout: Code, globals, heap, and stack live in distinct regions.
- Pointers as addresses: A pointer is just a number interpreted as an address.
- Endianness: Byte order affects how integers appear in memory dumps.
2.2 Why This Matters
Every bug in C eventually touches memory. If you can see addresses and bytes, pointer bugs stop being magic and start being mechanics.
2.3 Historical Context / Background
C was designed to expose memory. The language has no safety rail by design, which makes a strong memory model essential for real-world systems work.
2.4 Common Misconceptions
- “Pointers are special”: They are integers with an interpretation.
- “Stack and heap are the same”: They grow in opposite directions and behave differently.
3. Project Specification
3.1 What You Will Build
A CLI program that prints:
- Addresses of local and global variables
- Heap allocation addresses
- A hexdump of memory around a chosen pointer
- A simple stack frame trace using nested functions
3.2 Functional Requirements
- Print addresses of locals and globals.
- Allocate memory with
mallocand print its address. - Dump raw bytes for a chosen integer or struct.
- Show address changes across nested function calls.
3.3 Non-Functional Requirements
- Safety: Only read valid memory ranges.
- Usability: Output should be labeled and readable.
- Portability: Use standard C and POSIX where possible.
3.4 Example Usage / Output
$ ./memviz
[globals]
g_counter @ 0x5555555592a0 = 42
[stack]
main: x @ 0x7ffd2b8b8c3c = 10
foo : y @ 0x7ffd2b8b8c1c = 20
[heap]
heap block @ 0x55555556a2a0 (size=32)
[hexdump]
0x7ffd2b8b8c3c: 0a 00 00 00 14 00 00 00 ...
3.5 Real World Outcome
You run ./memviz and see concrete memory addresses for stack, heap, and globals. You can point to a byte sequence and explain which variable it represents, which is the exact skill used to debug crashes and memory corruption.
4. Solution Architecture
4.1 High-Level Design
main -> print globals -> call foo -> allocate heap -> hexdump bytes
4.2 Key Components
| Component | Responsibility | Key Decisions |
|---|---|---|
| Address printer | Print pointer values | Use %p with (void*) |
| Hexdump | Display bytes | Limit to safe range |
| Stack tracer | Nested calls | Use helper functions |
4.3 Data Structures
typedef struct {
const char *label;
const void *addr;
size_t size;
} MemBlock;
4.4 Algorithm Overview
Key Algorithm: Hexdump
- Take a pointer and size.
- Cast to
unsigned char *. - Print bytes in hex with offsets.
Complexity Analysis:
- Time: O(n)
- Space: O(1)
5. Implementation Guide
5.1 Development Environment Setup
cc -Wall -Wextra -O2 -g -o memviz memviz.c
5.2 Project Structure
memviz/
├── src/
│ └── memviz.c
├── tests/
│ └── smoke.sh
└── README.md
5.3 The Core Question You’re Answering
“Where do my variables actually live, and how can I prove it?”
5.4 Concepts You Must Understand First
Stop and research these before coding:
- Pointer basics
- What does
&xreturn? - Why must you use
%pto print addresses?
- What does
- Stack vs heap
- How do you allocate on heap with
malloc? - Why do stack addresses usually decrease in nested calls?
- How do you allocate on heap with
- Endianness
- How is
0x12345678stored in memory?
- How is
5.5 Questions to Guide Your Design
Before implementing, think through these:
- How will you avoid reading invalid memory?
- How many bytes should the hexdump display?
- What output format is easiest to understand?
5.6 Thinking Exercise
Trace by Hand
Write a small program with two nested functions and draw a stack diagram when the inner function runs. Where are the locals relative to each other?
5.7 The Interview Questions They’ll Ask
Prepare to answer these:
- “What is the difference between stack and heap memory?”
- “Why does
printf("%p")require casting to(void*)?” - “What is endianness and how can you detect it?”
5.8 Hints in Layers
Hint 1: Start with printing addresses
Print &x for a few locals and globals.
Hint 2: Add heap allocations
malloc a buffer and print its address.
Hint 3: Add a hexdump Print 16 bytes around a pointer for clarity.
5.9 Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Memory model | “Computer Systems: A Programmer’s Perspective” | Ch. 3 |
| Pointers | “Understanding and Using C Pointers” | Ch. 1-2 |
5.10 Implementation Phases
Phase 1: Foundation (2-3 hours)
Goals:
- Print addresses for locals and globals
Tasks:
- Add globals and locals and print their addresses.
Checkpoint: Output shows distinct address ranges.
Phase 2: Core Functionality (3-4 hours)
Goals:
- Heap allocation and hexdump
Tasks:
- Allocate memory with
mallocand print address. - Implement hexdump for a small buffer.
Checkpoint: Hexdump shows expected bytes.
Phase 3: Polish & Edge Cases (2-3 hours)
Goals:
- Stack frame visualization
Tasks:
- Add nested calls and print addresses per frame.
Checkpoint: Stack addresses show consistent ordering.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale |
|---|---|---|---|
| Hexdump size | 16 vs 64 bytes | 32 bytes | Readable and useful |
| Output format | Hex only vs hex+ASCII | Hex+ASCII | Easier to parse |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|---|---|---|
| Smoke Tests | Basic output | Run program once |
| Manual Checks | Visual verification | Compare addresses |
| Debugger Checks | Validate stack | gdb frame inspect |
6.2 Critical Test Cases
- No crashes when dumping nearby memory.
- Heap addresses differ from stack addresses.
- Endian check matches platform.
6.3 Test Data
int x = 0x12345678;
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution |
|---|---|---|
Using %x for addresses |
Garbage output | Use %p |
| Dumping invalid memory | Segfault | Limit to known buffers |
| Misreading endianness | Confusion | Dump known constant |
7.2 Debugging Strategies
- Use
gdbto print&xand compare. - Add
printflabels around each block.
7.3 Performance Traps
None significant; correctness is the focus.
8. Extensions & Challenges
8.1 Beginner Extensions
- Add command-line options to choose dump size.
- Print ASCII alongside hex bytes.
8.2 Intermediate Extensions
- Print the process memory map from
/proc/self/maps. - Add a small interactive prompt for inspecting pointers.
8.3 Advanced Extensions
- Visualize pointer chains (linked list traversal).
- Add a mini disassembler view of function bytes.
9. Real-World Connections
9.1 Industry Applications
- Debugging: Understand crashes and corruption.
- Security: Analyze memory layout and overflows.
9.2 Related Open Source Projects
- gdb: Debugger with memory inspection.
9.3 Interview Relevance
Memory layout questions are common in systems interviews.
10. Resources
10.1 Essential Reading
- “Computer Systems: A Programmer’s Perspective” - Ch. 3
- “Understanding and Using C Pointers” - Ch. 1-2
10.2 Video Resources
- OS memory layout lectures (any systems programming course)
10.3 Tools & Documentation
man 3 malloc: Heap allocationman 3 printf: Formatting pointers
10.4 Related Projects in This Series
- String Library: Builds on memory reasoning.
- Memory Allocator: Uses this model in depth.
11. Self-Assessment Checklist
11.1 Understanding
- I can explain stack vs heap addresses.
- I can describe endianness with an example.
- I can explain pointer printing.
11.2 Implementation
- The tool prints addresses and hexdumps.
- Output is readable and labeled.
- No invalid memory access occurs.
11.3 Growth
- I can use this tool to debug a pointer bug.
- I can explain the project in an interview.
12. Submission / Completion Criteria
Minimum Viable Completion:
- Prints stack, heap, and global addresses.
- Dumps bytes for a known variable.
Full Completion:
- Includes nested call visualization and labeled output.
Excellence (Going Above & Beyond):
- Adds
/proc/self/mapsintegration and interactive inspection.
This guide was generated from C_PROGRAMMING_COMPLETE_MASTERY.md. For the complete learning path, see the parent directory.