% c-memory-layout.tex — segments (.text/.rodata/.data/.bss/heap/stack) and where
% each variable kind lives, startup copy/zero, struct padding byte by byte,
% packed, sizeof/offsetof, endianness + safe buffer reads, integer promotion.
% Sources: docs/memos/c-systems.md (Q5, Q9, Q11, Q14), docs/memos/c-language-fundamentals.md
% (Q10-Q16), docs/memos/cs-memory-stack-heap.md, docs/school/qaa/c-systems.md
% (Q19, Q21, Q27, Q28, Q32), docs/school/teacher/plan.md ("can compute size by feel,
% cannot draw the layout").
% Build ONLY with: tools/print/print-sheet.py <this>.tex --dry-run
% @source: hiot monorepo, docs/school/sheets/c/c-memory-layout.tex — the SOURCE OF TRUTH; a copy anywhere else (e.g. artur.gurgul.pro) is regenerated from it, never edited
% @labels: area=c kind=concept level=senior platform=embedded new=no round=c-esp32-2026-09-24 topic=memory,language
% @tags: bss, data-segment, rodata, stack-overflow, struct-padding, alignment, packed, offsetof, endianness, ntohl, integer-promotion, crt0
\documentclass[8pt]{extarticle}
\usepackage{printup-sheet}

\lstdefinestyle{CSheet}{style=printup, language=C,
  morekeywords={uint8_t,uint16_t,uint32_t,size_t,bool,restrict,inline}}

\tikzset{
  seg/.style={draw=sheetGrey, minimum width=17mm, minimum height=4.6mm,
              font=\ttfamily\scriptsize, inner sep=1pt},
  byte/.style={draw=sheetGrey, minimum width=5.6mm, minimum height=5mm,
               font=\ttfamily\tiny, inner sep=0pt},
  pad/.style={byte, fill=black!12, text=black!45},
  lbl/.style={font=\scriptsize, text=black!75, inner sep=1pt},
  ttl/.style={font=\bfseries\scriptsize, inner sep=1pt},
}

\begin{document}

\sheettitle{Memory layout · padding · endianness · integer promotion}{c · memo}

\oneliner{Code and constants sit in \textbf{flash} (\texttt{.text},
\texttt{.rodata}); initialised globals in \texttt{.data} (RAM, with an image in
flash), zero ones in \texttt{.bss} (RAM, zeroed at boot, \emph{no flash});
\texttt{malloc} in the \textbf{heap}; locals on the \textbf{stack}. Struct
members sit at multiples of their alignment $\to$ padding. Byte order in memory
is the \textbf{endianness}. Arithmetic on anything narrower than \texttt{int}
happens \textbf{in \texttt{int}}.}

\begin{multicols}{2}

\section{Picture — segments on an MCU}
\begin{tikzpicture}[sheet]
  \node[ttl] at (0.85,2.35) {FLASH};
  \node[seg, fill=sheetBlue!10] (t) at (0.85,1.9) {.text code};
  \node[seg, fill=sheetBlue!18] (r) at (0.85,1.44) {.rodata const};
  \node[seg, fill=sheetOrange!15] (di) at (0.85,0.98) {.data image};
  \node[ttl] at (4.3,2.35) {RAM};
  \node[seg, fill=sheetOrange!15] (d) at (4.3,1.9) {.data};
  \node[seg, fill=sheetGreen!15] (b) at (4.3,1.44) {.bss};
  \node[seg, fill=sheetBrown!12] (h) at (4.3,0.98) {heap $\uparrow$};
  \node[seg, draw=sheetGrey!60, dashed, text=sheetGrey] (f) at (4.3,0.52) {free};
  \node[seg, fill=sheetRed!10] (s) at (4.3,0.06) {stack $\downarrow$};
  \draw[hot] (di.east) -- node[lbl, below, sloped]{copied at boot} (d.west);
  \node[lbl, anchor=west] at (5.3,1.44) {zeroed at boot};
  \node[lbl, anchor=west] at (5.3,0.98) {\texttt{malloc}};
  \node[lbl, anchor=west] at (5.3,0.06) {locals, frames};
  \node[lbl, anchor=west, text=sheetRed] at (5.3,0.52) {collide = corrupt};
  \node[note, anchor=north west, text width=30mm] at (-0.1,0.72) {ESP32: \texttt{.text} runs
    from flash via cache; \texttt{IRAM\_ATTR} $\to$ IRAM; each task's stack is
    carved from the heap.};
\end{tikzpicture}

\begin{tabular}{@{}lll@{}}
\toprule
\textbf{declaration} & \textbf{lives in} & \textbf{starts as}\\ \midrule
\texttt{int g = 5;} (file scope) & \texttt{.data} & 5 (flash copy)\\
\texttt{int z;} \ \texttt{int z = 0;} & \texttt{.bss} & \textbf{0} guaranteed\\
\texttt{static int n;} in a function & \texttt{.bss} & 0, keeps value\\
\texttt{const int k = 5;} (file) & \texttt{.rodata} & 5, flash\\
\texttt{"literal"} & \texttt{.rodata} & read-only\\
\texttt{int x;} in a function & stack/register & \textcolor{sheetRed}{indeterminate}\\
\texttt{const int c;} in a fn & stack & const $\neq$ flash\\
\texttt{p = malloc(n)} & heap (\texttt{p}: stack) & garbage\\
\bottomrule
\end{tabular}

\textbf{Before \texttt{main}} the startup code (\texttt{crt0}) copies the
\texttt{.data} image flash $\to$ RAM, zeroes \texttt{.bss}, sets the stack
pointer. \texttt{size app.elf} / \texttt{idf.py size} print
\texttt{text}/\texttt{data}/\texttt{bss}.
\textbf{Stack overflow} (your Q21): recursion or a big local
(\texttt{uint8\_t buf[8192]}); no MMU $\to$ \emph{no fault}, it silently
overwrites the neighbour. Detect: \texttt{configCHECK\_FOR\_STACK\_OVERFLOW 2}
(canary), \texttt{uxTaskGetStackHighWaterMark()}.

\section{Struct padding — the rules}
\begin{itemize}
  \item Each member starts at a multiple of its \textbf{alignment} (usually
        its size); the compiler inserts padding before it.
  \item \texttt{sizeof} rounds up to the largest alignment, so arrays of the
        struct stay aligned: \texttt{\{uint32\_t a; uint8\_t b;\}} = 8.
  \item Fix: order members \textbf{largest $\to$ smallest}. Pin wire formats:
        \texttt{\_Static\_assert(sizeof(struct Msg) == 8, "layout");}
        \texttt{offsetof(struct Msg, len)} (\texttt{<stddef.h>}).
  \item Padding bytes are unspecified: don't \texttt{memcmp} structs;
        \texttt{memset} before sending one (info leak).
  \item \texttt{\_\_attribute\_\_((packed))}: no padding, but members may be
        unaligned $\to$ slower byte-wise code, and \texttt{\&m.id} used as a
        \texttt{uint32\_t *} faults: \textbf{ESP32 \texttt{LoadStoreAlignment}
        panic}, Cortex-M0 HardFault (M3/M4 tolerate plain \texttt{LDR}).
\end{itemize}

\section{Likely questions}
\begin{enumerate}
  \item Why does \texttt{.bss} cost no flash? — only its size is stored.
  \item Read a u16 off the wire? — \texttt{p[0] << 8 | p[1]}, or \texttt{memcpy} + \texttt{ntohs}.
  \item \texttt{i = -1; i < sizeof buf}? — false: \texttt{i} becomes \texttt{size\_t}.
\end{enumerate}

\columnbreak

\section{Picture — \texttt{struct Msg} byte by byte (32-bit)}
\begin{tikzpicture}[sheet]
  \node[font=\ttfamily\scriptsize, anchor=west] at (-0.1,0.55)
    {\{ uint8\_t type; uint32\_t id; uint8\_t flags; uint16\_t len; \}};
  \foreach \i/\s/\t in {0/byte/type,1/pad/,2/pad/,3/pad/,4/byte/id,5/byte/id,6/byte/id,7/byte/id,
                        8/byte/flg,9/pad/,10/byte/len,11/byte/len}{
    \node[\s] (x\i) at (0.3+0.56*\i,0) {\t};
    \node[font=\ttfamily\tiny, text=sheetGrey] at (0.3+0.56*\i,-0.38) {\i};
  }
  \foreach \i in {0} \node[byte, fill=sheetBlue!15] at (0.3+0.56*\i,0) {type};
  \foreach \i in {4,5,6,7} \node[byte, fill=sheetOrange!18] at (0.3+0.56*\i,0) {id};
  \node[byte, fill=sheetGreen!18] at (0.3+0.56*8,0) {flg};
  \foreach \i in {10,11} \node[byte, fill=sheetBrown!18] at (0.3+0.56*\i,0) {len};
  \node[lbl, anchor=west] at (6.9,0) {\textbf{12 B}};
  \draw[decorate, decoration={brace, mirror, amplitude=3pt}] (0.58,-0.55) -- (2.0,-0.55)
    node[midway, below=2pt, lbl]{pad: \texttt{id} needs \% 4};
  \draw[decorate, decoration={brace, mirror, amplitude=3pt}] (4.8,-0.55) -- (5.35,-0.55)
    node[midway, below=2pt, lbl]{\% 2};
  % reordered
  \node[font=\ttfamily\scriptsize, anchor=west] at (-0.1,-1.35)
    {reordered: \{ uint32\_t id; uint16\_t len; uint8\_t type, flags; \}};
  \foreach \i in {0,1,2,3} \node[byte, fill=sheetOrange!18] at (0.3+0.56*\i,-1.85) {id};
  \foreach \i in {4,5} \node[byte, fill=sheetBrown!18] at (0.3+0.56*\i,-1.85) {len};
  \node[byte, fill=sheetBlue!15] at (0.3+0.56*6,-1.85) {type};
  \node[byte, fill=sheetGreen!18] at (0.3+0.56*7,-1.85) {flg};
  \node[lbl, anchor=west] at (4.65,-1.85) {\textbf{8 B}, 0 padding};
  \node[lbl, anchor=west] at (4.65,-2.25) {packed original: 8 B, \texttt{id} at 1!};
\end{tikzpicture}

\section{Picture — \texttt{0x01020304} at address 0x100}
\begin{tikzpicture}[sheet]
  \foreach \i in {0,1,2,3} \node[font=\ttfamily\tiny, text=sheetGrey] at (2.0+0.75*\i,0.4) {0x10\i};
  \node[lbl, anchor=east] at (1.55,0) {little (ESP32, x86, ARM)};
  \foreach \i/\v in {0/04,1/03,2/02,3/01} \node[byte, minimum width=7mm, fill=sheetGreen!12] at (2.0+0.75*\i,0) {\v};
  \node[lbl, anchor=east] at (1.55,-0.55) {big = network order};
  \foreach \i/\v in {0/01,1/02,2/03,3/04} \node[byte, minimum width=7mm, fill=sheetBlue!12] at (2.0+0.75*\i,-0.55) {\v};
  \node[lbl, anchor=west, align=left] at (4.95,-0.27) {little: \textbf{L}east\\significant first};
\end{tikzpicture}

\section{Example — big-endian u32 from a buffer}
\begin{lstlisting}[style=CSheet]
uint32_t rd_be32(const uint8_t *p) {   // any alignment
  return (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16
       | (uint32_t)p[2] << 8  | p[3];  // cast! p[0]<<24 is int
}
uint32_t v; memcpy(&v, buf + 1, 4); v = ntohl(v); // same
uint32_t bad = *(uint32_t *)(buf + 1); // unaligned+aliasing: UB
\end{lstlisting}
Shifts act on \emph{values}: endian-independent. \texttt{hton*/ntoh*} swap on little-endian only.

\section{Integer promotion + usual conversions}
\begin{itemize}
  \item Narrower than \texttt{int} (\texttt{uint8\_t}, \texttt{uint16\_t},
        \texttt{char}, bit-fields) $\to$ \texttt{int} first.
        \texttt{uint8\_t a=200, b=100;} \texttt{a+b} = \textbf{300}; only
        \texttt{uint8\_t r = a+b;} truncates to 44.
  \item \texttt{\textasciitilde x} on \texttt{uint8\_t 0xFF} = \texttt{int}
        \texttt{0xFFFFFF00} $\neq$ 0 — write \texttt{(uint8\_t)\textasciitilde x}.
  \item \texttt{uint16\_t} \texttt{65535*65535} is \texttt{int} math $\to$
        signed overflow, \textbf{UB}. Cast: \texttt{(uint32\_t)a*b}.
  \item Same rank, mixed sign $\to$ \textbf{unsigned wins}: \texttt{-1 < 1u} is
        false; \texttt{int i=-1; i < sizeof buf} is false (\texttt{size\_t}).
\end{itemize}

\section{Interview traps}
\begin{itemize}
  \trap{\textbf{Q11:} globals are never on the stack; \texttt{.bss} is
        \textbf{0}, not random. Only locals start indeterminate.}
  \trap{\textbf{Q5/Q19:} draw the layout — offsets, pads, trailing pad.}
  \trap{\textbf{Q9:} you reversed it: bytes \texttt{01 02 03 04} read as
        \texttt{uint32\_t} on ESP32 = \textbf{\texttt{0x04030201}}.}
  \trap{\textbf{Q14:} right output, wrong reason — no wrap, it is \texttt{int} 300.}
\end{itemize}

\section{Remember}
\textbf{Flash keeps code, constants and the .data image; .bss costs no flash.
Big first, no padding. Little-endian = little end first. Small ints become int.}

\end{multicols}

\noindent{\footnotesize\color{sheetGrey}\textit{Related:} c-pointers-const-restrict · c-volatile-atomics-isr · linker script / \texttt{IRAM\_ATTR} · Swift \texttt{MemoryLayout} size/stride/alignment}

\end{document}
