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:

  1. Visualize stack and heap addresses at runtime.
  2. Explain how variables map to memory.
  3. Print raw bytes to understand endianness.
  4. 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

  1. Print addresses of locals and globals.
  2. Allocate memory with malloc and print its address.
  3. Dump raw bytes for a chosen integer or struct.
  4. 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

  1. Take a pointer and size.
  2. Cast to unsigned char *.
  3. 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:

  1. Pointer basics
    • What does &x return?
    • Why must you use %p to print addresses?
  2. Stack vs heap
    • How do you allocate on heap with malloc?
    • Why do stack addresses usually decrease in nested calls?
  3. Endianness
    • How is 0x12345678 stored in memory?

5.5 Questions to Guide Your Design

Before implementing, think through these:

  1. How will you avoid reading invalid memory?
  2. How many bytes should the hexdump display?
  3. 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:

  1. “What is the difference between stack and heap memory?”
  2. “Why does printf("%p") require casting to (void*)?”
  3. “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:

  1. 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:

  1. Allocate memory with malloc and print address.
  2. Implement hexdump for a small buffer.

Checkpoint: Hexdump shows expected bytes.

Phase 3: Polish & Edge Cases (2-3 hours)

Goals:

  • Stack frame visualization

Tasks:

  1. 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

  1. No crashes when dumping nearby memory.
  2. Heap addresses differ from stack addresses.
  3. 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 gdb to print &x and compare.
  • Add printf labels 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.
  • 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 allocation
  • man 3 printf: Formatting pointers
  • 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/maps integration and interactive inspection.

This guide was generated from C_PROGRAMMING_COMPLETE_MASTERY.md. For the complete learning path, see the parent directory.