<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pablo GS</title>
    <description>The latest articles on DEV Community by Pablo GS (@pablogs).</description>
    <link>https://dev.to/pablogs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3957976%2Fa148d74f-0c1f-43fb-bf77-6e380bb8c6c0.png</url>
      <title>DEV Community: Pablo GS</title>
      <link>https://dev.to/pablogs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pablogs"/>
    <language>en</language>
    <item>
      <title>Hello, Array: malloc, free and Manual Bookkeeping</title>
      <dc:creator>Pablo GS</dc:creator>
      <pubDate>Fri, 29 May 2026 07:37:41 +0000</pubDate>
      <link>https://dev.to/pablogs/hello-array-malloc-free-and-manual-bookkeeping-4062</link>
      <guid>https://dev.to/pablogs/hello-array-malloc-free-and-manual-bookkeeping-4062</guid>
      <description>&lt;p&gt;&lt;em&gt;Post 1 of the Dynamic Arrays in C series · &lt;a href="https://github.com/ansuzgs/dynamic-arrays-c/blob/main/src/post_01.c" rel="noopener noreferrer"&gt;Full source code&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem No One Starts With
&lt;/h2&gt;

&lt;p&gt;You have five integers. You put them in an array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done. C gives you a contiguous chunk of 20 bytes on the stack, indexed from 0 to 4, and life is good.&lt;/p&gt;

&lt;p&gt;Now your user wants to add a sixth integer. What do you do?&lt;/p&gt;

&lt;p&gt;You can't resize a stack array. Its size was baked into the binary at compile time, the compiler saw &lt;code&gt;5&lt;/code&gt;, calculated 20 bytes, and that's the space your function's stack frame has. There's no negotiation. You could declare &lt;code&gt;int numbers[1000]&lt;/code&gt; and hope it's big enough, but hope is not a memory management strategy.&lt;/p&gt;

&lt;p&gt;You could use a variable-length array (&lt;code&gt;int numbers[n]&lt;/code&gt;), but that just shifts the problem: &lt;code&gt;n&lt;/code&gt; is still fixed once you enter the function. Worse, VLAs live on the stack, which is limited to a few megabytes. Store a million integers and you blow the stack with no graceful recovery.&lt;/p&gt;

&lt;p&gt;The real solution lives on the heap. &lt;code&gt;malloc&lt;/code&gt; lets you ask the operating system for a chunk of memory at runtime, any size you want, limited only by available RAM. But malloc gives you raw bytes and a pointer. No size tracking. No bounds checking. No "how full am I?" bookkeeping. You get the memory and the responsibility.&lt;/p&gt;

&lt;p&gt;This is where every dynamic array begins: not with a clever data structure, but with a basic question of &lt;em&gt;bookkeeping&lt;/em&gt;. Who tracks how many elements you've stored? Who tracks how many you &lt;em&gt;could&lt;/em&gt; store? Who makes sure the memory gets freed when you're done?&lt;/p&gt;

&lt;p&gt;In C, the answer is always the same: you do.&lt;/p&gt;

&lt;p&gt;This post builds the simplest possible dynamic array, one that holds integers, has a fixed capacity, and does exactly three things: create, push, and destroy. No automatic growth (that's &lt;a href="https://pablogs.dev/posts/post-02-growing-pain/index.md" rel="noopener noreferrer"&gt;Post 2&lt;/a&gt;), no generics (Post 4), no error recovery (Post 6). Just the raw skeleton that everything else builds on.&lt;/p&gt;

&lt;p&gt;By the end, you'll understand two things most C tutorials skip: why the metadata struct exists, and why the order in which you call &lt;code&gt;free&lt;/code&gt; matters.&lt;/p&gt;

&lt;p&gt;Let's allocate some memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Struct: What an Array Knows About Itself
&lt;/h2&gt;

&lt;p&gt;A raw &lt;code&gt;malloc&lt;/code&gt; call returns &lt;code&gt;void *&lt;/code&gt;, a pointer to bytes with no meaning attached. If you allocate space for 10 integers, nobody remembers that number except you. The instant you lose track of the capacity, you're writing bugs.&lt;/p&gt;

&lt;p&gt;So the first thing a dynamic array needs isn't data. It's &lt;em&gt;metadata&lt;/em&gt;: a small struct that sits alongside the data and remembers the bookkeeping details.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="cm"&gt;/* Pointer to the heap buffer holding elements  */&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt;  &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="cm"&gt;/* How many elements have been stored           */&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt;  &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="cm"&gt;/* How many elements the buffer can hold        */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;IntArray&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three fields. This is the minimum viable bookkeeping:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;data&lt;/code&gt; is a pointer to the actual heap allocation where elements live. It's the result of a &lt;code&gt;malloc(capacity * sizeof(int))&lt;/code&gt; call. When you access &lt;code&gt;arr-&amp;gt;data[3]&lt;/code&gt;, you're reading the fourth integer in that allocation.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;size&lt;/code&gt; tracks how many elements the user has actually pushed. It starts at 0 and increments with every &lt;code&gt;array_push&lt;/code&gt;. It is &lt;em&gt;not&lt;/em&gt; the same as capacity, this distinction is the single most important concept in dynamic array design.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;capacity&lt;/code&gt; tracks how many elements the allocation &lt;em&gt;can hold&lt;/em&gt;. If you malloced space for 10 integers, capacity is 10, even if size is only 3. The gap between size and capacity is wasted memory, we allocated it but aren't using it yet. Managing that gap is the art of dynamic arrays.&lt;/p&gt;

&lt;p&gt;Think of it like a parking garage. &lt;code&gt;capacity&lt;/code&gt; is the number of parking spots. &lt;code&gt;size&lt;/code&gt; is the number of cars currently parked. The garage exists at a specific address (&lt;code&gt;data&lt;/code&gt;). You can have an empty garage (size=0, capacity=100) or a full one (size=100, capacity=100), but you can never park more cars than spots, unless you build a bigger garage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/post_01_state.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/post_01_state.svg" alt="State" width="800" height="400"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;Diagram showing the IntArray metadata struct with a pointer to a contiguous heap buffer of 5 integer slots.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What malloc Actually Does
&lt;/h2&gt;

&lt;p&gt;Before looking at the implementation, it's worth understanding what happens when you call &lt;code&gt;malloc(20)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You're not asking the OS for exactly 20 bytes. The C runtime's allocator (glibc's &lt;code&gt;ptmalloc2&lt;/code&gt; on Linux, &lt;code&gt;jemalloc&lt;/code&gt; on FreeBSD, etc.) maintains pools of pre-allocated memory called &lt;em&gt;arenas&lt;/em&gt;. When you call &lt;code&gt;malloc(20)&lt;/code&gt;, the allocator finds a free block in one of its pools, marks it as used, and returns its address. If no pool has room, the allocator requests more memory from the kernel via &lt;code&gt;sbrk&lt;/code&gt; or &lt;code&gt;mmap&lt;/code&gt;, but this is the expensive path, and it happens rarely.&lt;/p&gt;

&lt;p&gt;The returned pointer has a hidden &lt;em&gt;header&lt;/em&gt; just before it (typically 8–16 bytes) where the allocator stores metadata: the block's size, whether it's in use, and pointers to adjacent free blocks. This is how &lt;code&gt;free&lt;/code&gt; knows how many bytes to release, you never tell it the size, because the allocator already recorded it.&lt;/p&gt;

&lt;pre&gt;
What malloc returns:        What actually exists in memory:
                            ┌──────────────────┐
                            │ allocator header │  (hidden, 8-16 bytes)
    ptr ──────────────────► ├──────────────────┤
                            │                  │
                            │   your 20 bytes  │
                            │                  │
                            └──────────────────┘
&lt;/pre&gt;
 

&lt;p&gt;This has practical consequences. Every &lt;code&gt;malloc&lt;/code&gt; call costs not just the bytes you asked for, but also the overhead of that header. If you allocate a million 4-byte blocks, you're actually using 12–20 bytes per block, the 4 bytes you wanted plus the hidden header. For our dynamic array, this is why we make &lt;em&gt;two&lt;/em&gt; allocations (one for the struct, one for the buffer) instead of millions of individual &lt;code&gt;malloc(sizeof(int))&lt;/code&gt; calls: fewer allocations means less overhead.&lt;/p&gt;

&lt;p&gt;It also means that &lt;code&gt;free&lt;/code&gt; doesn't need to know the size of the allocation, it reads the header. But &lt;code&gt;free&lt;/code&gt; doesn't zero the memory or return it to the OS. The block is just marked as available in the allocator's free list. The bytes remain there, with their old values, until something else overwrites them. This is why use-after-free bugs are insidious: the data &lt;em&gt;looks&lt;/em&gt; valid long after you've freed it.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;The implementation compiles with zero warnings under &lt;code&gt;gcc -Wall -Wextra -Wpedantic -std=c11&lt;/code&gt; and runs without leaks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The full source, including a &lt;code&gt;main()&lt;/code&gt; that demonstrates every operation step by step, is available &lt;a href="https://github.com/ansuzgs/dynamic-arrays-c/blob/main/src/post_01.c" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;. Below are the essential pieces.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Lifecycle: Create and Destroy
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;IntArray&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;array_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"array_create: capacity must be &amp;gt; 0&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;IntArray&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IntArray&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;       &lt;span class="cm"&gt;/* allocation #1 */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"array_create: failed to allocate struct&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;     &lt;span class="cm"&gt;/* allocation #2 */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"array_create: failed to allocate buffer&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="cm"&gt;/* Don't leak the struct if the buffer fails */&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Notice the error handling between the two allocations. If the first &lt;code&gt;malloc&lt;/code&gt; succeeds but the second fails, we have a partially-constructed object: a struct on the heap with an invalid &lt;code&gt;data&lt;/code&gt; pointer. If we returned NULL without freeing the struct, those 24 bytes (three fields on a 64-bit system: one pointer + two &lt;code&gt;size_t&lt;/code&gt;) would be leaked. The &lt;code&gt;free(arr)&lt;/code&gt; call before the &lt;code&gt;return NULL&lt;/code&gt; prevents that. This pattern, clean up everything you've allocated so far when a later step fails, is the foundation of resource cleanup in C. You'll see it scaled up in Post 6 when we discuss error handling strategies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;array_destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IntArray&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="cm"&gt;/* 1. free the element buffer                    */&lt;/span&gt;
    &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="cm"&gt;/*    (defensive: prevent use-after-free)        */&lt;/span&gt;
    &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="cm"&gt;/* 2. free the struct itself                     */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two allocations in &lt;code&gt;array_create&lt;/code&gt;, two frees in &lt;code&gt;array_destroy&lt;/code&gt;. The symmetry is intentional and the order is not negotiable, more on that below.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operations: Push and Get
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;array_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IntArray&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"array_push: NULL array&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"array_push: full (size=%zu, capacity=%zu). "&lt;/span&gt;
                &lt;span class="s"&gt;"Cannot add %d.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;array_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;IntArray&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;array_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;IntArray&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;     &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;array_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;IntArray&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Push writes to &lt;code&gt;arr-&amp;gt;data[arr-&amp;gt;size]&lt;/code&gt; and increments &lt;code&gt;size&lt;/code&gt;. Two lines of actual logic; the rest is validation. The expression &lt;code&gt;arr-&amp;gt;data[arr-&amp;gt;size]&lt;/code&gt; works because array indexing in C is pointer arithmetic: &lt;code&gt;arr-&amp;gt;data[n]&lt;/code&gt; is equivalent to &lt;code&gt;*(arr-&amp;gt;data + n)&lt;/code&gt;, which means "start at the address in &lt;code&gt;data&lt;/code&gt;, move forward &lt;code&gt;n * sizeof(int)&lt;/code&gt; bytes, and dereference." As long as &lt;code&gt;n &amp;lt; capacity&lt;/code&gt;, that address is within our allocation.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;array_get&lt;/code&gt; returns the value through an output pointer (&lt;code&gt;out&lt;/code&gt;) instead of returning it directly. This is because we need two channels: the value itself and whether the operation succeeded. Returning &lt;code&gt;int&lt;/code&gt; for both the value and the error code would be ambiguous, is &lt;code&gt;-1&lt;/code&gt; an error or a legitimate stored value? The output pointer pattern separates these concerns cleanly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Driving It All
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IntArray&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;array_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="cm"&gt;/* This will fail — array is full */&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="cm"&gt;/* returns -1 */&lt;/span&gt;

    &lt;span class="cm"&gt;/* Read back */&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;array_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;array_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"arr[%zu] = %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;array_destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compile and run it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcc &lt;span class="nt"&gt;-Wall&lt;/span&gt; &lt;span class="nt"&gt;-Wextra&lt;/span&gt; &lt;span class="nt"&gt;-Wpedantic&lt;/span&gt; &lt;span class="nt"&gt;-std&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;c11 &lt;span class="nt"&gt;-o&lt;/span&gt; post_01 post_01.c
./post_01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;pre&gt;
╔══════════════════════════════════════════════════════╗
║  After push(30)                                      ║
╠══════════════════════════════════════════════════════╣
║  size = 3      capacity = 5      elem = 4 bytes      ║
║  data = 0x56fbb1d642d0  (heap)                       ║
╠══════════════════════════════════════════════════════╣
║  ┌──────┌──────┌──────┌──────┌──────┐                ║
║  │   10 │   20 │   30 │  ·   │  ·   │                ║
║  └──────└──────└──────└──────└──────┘                ║
║     0      1      2      3      4                    ║
║                   ▲ size=3                           ║
╠══════════════════════════════════════════════════════╣
║  12B used / 20B allocated = 60.0% utilization        ║
║  8B wasted (40.0%)                                   ║
╚══════════════════════════════════════════════════════╝
&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;Screenshot of the ASCII visualization output showing the array after pushing 3 elements (10, 20, 30) into a capacity-5 array, with occupied slots and empty slots clearly distinguished.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Walking Through the Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Two Allocations, Two Frees
&lt;/h3&gt;

&lt;p&gt;The most important pattern in this file is the symmetry between &lt;code&gt;array_create&lt;/code&gt; and &lt;code&gt;array_destroy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;array_create&lt;/code&gt; performs two allocations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IntArray&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;          &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;    &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="err"&gt;×&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;array_destroy&lt;/code&gt; performs two frees, in reverse order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;
&lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reverse order is not a stylistic preference, it's a correctness requirement. Let's trace what happens if you swap them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* WRONG — undefined behavior */&lt;/span&gt;
&lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;           &lt;span class="cm"&gt;/* struct memory returned to allocator */&lt;/span&gt;
&lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="cm"&gt;/* arr is now a dangling pointer — reading arr-&amp;gt;data
                        is an invalid memory access */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After &lt;code&gt;free(arr)&lt;/code&gt;, the memory at &lt;code&gt;arr&lt;/code&gt; has been returned to the allocator. It could be reused by the very next &lt;code&gt;malloc&lt;/code&gt; call, even one happening on another thread. Reading &lt;code&gt;arr-&amp;gt;data&lt;/code&gt; at that point might return the original pointer value (if the memory hasn't been touched yet), or it might return garbage (if the allocator has overwritten those bytes with its own bookkeeping data for the free list). Either way, it's undefined behavior. Valgrind would flag this as "Invalid read of size 8" (the size of a pointer).&lt;/p&gt;

&lt;p&gt;The general principle: when you have nested allocations (a struct that owns pointers to other allocations), free from the inside out, innermost allocations first, outermost last.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Defensive NULL Assignment
&lt;/h3&gt;

&lt;p&gt;After freeing the data buffer, we set &lt;code&gt;arr-&amp;gt;data = NULL&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="cm"&gt;/* defensive */&lt;/span&gt;
&lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this simple code, nothing touches &lt;code&gt;arr-&amp;gt;data&lt;/code&gt; between the two frees, so the NULL assignment does nothing. But in more complex code, with error handlers, callbacks, or cleanup functions that might run between those two lines, the NULL acts as a safety net. If anything tries to dereference &lt;code&gt;arr-&amp;gt;data&lt;/code&gt; after the first free, it hits a NULL pointer dereference instead of a use-after-free. A NULL dereference is a loud, immediate crash with a clear stack trace. A use-after-free is a silent corruption that might not manifest until thousands of lines later. You trade one bug for a more debuggable bug.&lt;/p&gt;

&lt;p&gt;This pattern is sometimes called &lt;em&gt;defensive clearing&lt;/em&gt; or &lt;em&gt;poisoning&lt;/em&gt;. Some codebases go further and zero the entire struct before the final free (&lt;code&gt;memset(arr, 0, sizeof(*arr))&lt;/code&gt;), though that has a cost: the compiler might optimize it away if it can prove nothing reads the struct afterward (since reading freed memory is UB). Post 6 addresses this in the context of a broader error-handling strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pointer Arithmetic Inside Push
&lt;/h3&gt;

&lt;p&gt;The line that does the actual work in &lt;code&gt;array_push&lt;/code&gt; is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single line involves three dereferences:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;arr&lt;/code&gt; is dereferenced to access the struct on the heap&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;arr-&amp;gt;data&lt;/code&gt; is dereferenced to get the base address of the buffer&lt;/li&gt;
&lt;li&gt;The result is indexed by &lt;code&gt;arr-&amp;gt;size&lt;/code&gt; to compute the write address
On a 64-bit system, the write address is calculated as:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;write_address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;size &amp;gt;= capacity&lt;/code&gt; check before this line guarantees that &lt;code&gt;write_address&lt;/code&gt; falls within our allocated buffer. Without it, we'd be writing past the end of our allocation, a heap buffer overflow. Depending on what lives past our allocation, this could corrupt the allocator's metadata (causing a crash in a later, unrelated &lt;code&gt;malloc&lt;/code&gt; or &lt;code&gt;free&lt;/code&gt;), overwrite another variable's data (causing impossible-looking bugs), or hit a guard page (causing an immediate segfault). AddressSanitizer (&lt;code&gt;gcc -fsanitize=address&lt;/code&gt;) catches these instantly, and it's worth compiling with it during development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts and Tradeoffs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Stack vs Heap for the Metadata Struct
&lt;/h3&gt;

&lt;p&gt;Our &lt;code&gt;array_create&lt;/code&gt; allocates the &lt;code&gt;IntArray&lt;/code&gt; struct on the heap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;IntArray&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IntArray&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why not put it on the stack? You could write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;IntArray&lt;/span&gt; &lt;span class="nf"&gt;array_create_stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IntArray&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="cm"&gt;/* returns a copy */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, and it's actually slightly faster, stack allocation is just a pointer decrement on the stack pointer register, while &lt;code&gt;malloc&lt;/code&gt; navigates free lists. But it changes the API in subtle and dangerous ways.&lt;/p&gt;

&lt;p&gt;The caller receives a &lt;em&gt;copy&lt;/em&gt; of the struct. If they pass that copy to &lt;code&gt;array_push&lt;/code&gt;, the function modifies its &lt;em&gt;own&lt;/em&gt; copy of &lt;code&gt;size&lt;/code&gt;, the caller's &lt;code&gt;size&lt;/code&gt; remains unchanged. You'd need to pass by pointer everywhere: &lt;code&gt;array_push(&amp;amp;arr, value)&lt;/code&gt;. That's workable, but easy to forget, and the compiler won't warn you.&lt;/p&gt;

&lt;p&gt;More seriously, the stack struct's lifetime is tied to its scope. If you return it from a function, you're returning a copy (fine). If you store a pointer to it and the function returns, that pointer is dangling (catastrophic). Heap allocation gives you a stable pointer that survives function boundaries, can be stored in other data structures, and has a clear ownership model: whoever holds the pointer is responsible for calling &lt;code&gt;array_destroy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For a library API, heap allocation is the standard choice. You'll see this pattern in virtually every C library: &lt;code&gt;thing_create()&lt;/code&gt; returns a pointer, &lt;code&gt;thing_destroy()&lt;/code&gt; frees it. The tradeoff is explicit: you trade a few nanoseconds of &lt;code&gt;malloc&lt;/code&gt; overhead and the burden of manual cleanup for an API that's unambiguous about ownership and lifetime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Waste: The Capacity Problem
&lt;/h3&gt;

&lt;p&gt;When you create an array with capacity 10 and store 3 elements, you're wasting 7 slots × 4 bytes = 28 bytes. That's a 70% waste ratio. For our post, where we push 5 elements into a capacity-5 array, waste drops to 0% by the end, but there's a window where we're paying for memory we haven't used yet.&lt;/p&gt;

&lt;p&gt;Is this bad? It depends on scale. For a single array, 28 bytes is negligible. For a million small arrays in a memory-constrained embedded system, it might matter. For one large array, the waste percentage drops to near zero as you fill it.&lt;/p&gt;

&lt;p&gt;The real question is: &lt;em&gt;what capacity should you start with?&lt;/em&gt; If you pick too small (capacity=1), you'll need to reallocate on every push once we add growth in &lt;a href="https://pablogs.dev/posts/post-02-growing-pain/index.md" rel="noopener noreferrer"&gt;Post 2&lt;/a&gt;, each reallocation involves copying all existing elements. If you pick too large (capacity=10000), you waste memory on arrays that only hold 5 elements. The tension between &lt;em&gt;time&lt;/em&gt; (fewer reallocations) and &lt;em&gt;space&lt;/em&gt; (less waste) is the central tradeoff of dynamic arrays, and it leads directly to the growth factor analysis in Post 3.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Return Codes, Not Assertions
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;array_push&lt;/code&gt; returns 0 on success and -1 on failure. It doesn't abort the program. This is a conscious design decision: the &lt;em&gt;caller&lt;/em&gt; should decide what to do when a push fails. Maybe the caller wants to log and continue. Maybe they want to resize and retry. Maybe they want to exit. By returning an error code, we give the caller that choice.&lt;/p&gt;

&lt;p&gt;The alternative, &lt;code&gt;assert(arr-&amp;gt;size &amp;lt; arr-&amp;gt;capacity)&lt;/code&gt;, kills the program with no recovery. That's appropriate for programmer errors (invariants that should never be violated if the code is correct), but not for runtime conditions like "the array is full." A full array isn't a bug, it's a foreseeable state that the program should handle.&lt;/p&gt;

&lt;p&gt;There's a subtlety here about &lt;code&gt;errno&lt;/code&gt; and error reporting that's worth flagging. Our current approach, printing to stderr and returning -1, is fine for a learning exercise, but production code would typically set &lt;code&gt;errno&lt;/code&gt; or return a richer error type. We'll discuss the full spectrum of error handling strategies in Post 6.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pointer Ownership: The Contract
&lt;/h3&gt;

&lt;p&gt;There's an implicit contract in our API that no comment or type annotation enforces: the pointer returned by &lt;code&gt;array_create&lt;/code&gt; is &lt;em&gt;owned&lt;/em&gt; by the caller. Ownership means exactly one thing: the owner is responsible for calling &lt;code&gt;array_destroy&lt;/code&gt; on it. Nobody else should free it. Nobody should free parts of it (&lt;code&gt;arr-&amp;gt;data&lt;/code&gt;) independently. And once &lt;code&gt;array_destroy&lt;/code&gt; has been called, every copy of that pointer becomes invalid.&lt;/p&gt;

&lt;p&gt;C has no language-level mechanism to enforce this. Rust has &lt;code&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; and affine types. C++ has &lt;code&gt;std::unique_ptr&lt;/code&gt;. In C, ownership is a convention, one you communicate through documentation, naming (&lt;code&gt;create&lt;/code&gt;/&lt;code&gt;destroy&lt;/code&gt; pairs), and discipline.&lt;/p&gt;

&lt;p&gt;The bugs that arise from violating ownership are among the worst in C:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Double free&lt;/strong&gt;: two parts of the code both think they own the pointer and both call &lt;code&gt;array_destroy&lt;/code&gt;. The second free corrupts the allocator's metadata.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use after free&lt;/strong&gt;: one part of the code frees the pointer while another still holds a copy and keeps using it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory leak&lt;/strong&gt;: nobody frees the pointer because everyone assumes someone else will.
In this simple post, ownership is obvious, &lt;code&gt;main&lt;/code&gt; creates, &lt;code&gt;main&lt;/code&gt; destroys. In larger programs with shared data structures, callbacks, and multithreading, ownership becomes the hardest problem in C. We'll revisit this in Post 7 when we add function pointers and destructors for element types.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try This and Watch It Fail
&lt;/h2&gt;

&lt;p&gt;Before moving on, try these experiments with the code:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Experiment 1: The Memory Leak.&lt;/strong&gt; In &lt;code&gt;array_destroy&lt;/code&gt;, comment out &lt;code&gt;free(arr-&amp;gt;data)&lt;/code&gt;. Compile and run under valgrind (&lt;code&gt;valgrind --leak-check=full ./post_01&lt;/code&gt;). You'll see "definitely lost: 20 bytes", that's the orphaned buffer. The struct was freed, but the buffer it pointed to was not. This is &lt;em&gt;exactly&lt;/em&gt; the bug the knowledge test asks about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Experiment 2: Use After Free.&lt;/strong&gt; In &lt;code&gt;main&lt;/code&gt;, add &lt;code&gt;printf("%d\n", arr-&amp;gt;data[0]);&lt;/code&gt; &lt;em&gt;after&lt;/em&gt; &lt;code&gt;array_destroy(arr)&lt;/code&gt;. Compile and run. It might print &lt;code&gt;10&lt;/code&gt;. It might print garbage. It might crash. That's undefined behavior, the data is freed, but the memory hasn't necessarily been overwritten yet. Valgrind would flag this as "Invalid read of size 4."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Experiment 3: Buffer Overflow.&lt;/strong&gt; Remove the &lt;code&gt;size &amp;gt;= capacity&lt;/code&gt; check in &lt;code&gt;array_push&lt;/code&gt;. Push 100 elements into a capacity-5 array. Compile with AddressSanitizer (&lt;code&gt;gcc -fsanitize=address&lt;/code&gt;) and watch it detect the heap-buffer-overflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Experiment 4: The Double Free.&lt;/strong&gt; In &lt;code&gt;main&lt;/code&gt;, call &lt;code&gt;array_destroy(arr)&lt;/code&gt; twice. On many systems the second call will crash with "double free or corruption." Some allocators detect this immediately; others corrupt silently and crash later. This is why our &lt;code&gt;array_destroy&lt;/code&gt; accepts NULL gracefully, if you set &lt;code&gt;arr = NULL&lt;/code&gt; after the first destroy, the second call becomes a no-op.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knowledge Test
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What happens if you call &lt;code&gt;free(arr)&lt;/code&gt; but forget to call &lt;code&gt;free(arr-&amp;gt;data)&lt;/code&gt; first?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The struct is returned to the heap allocator, but the buffer it pointed to, the &lt;code&gt;capacity * sizeof(int)&lt;/code&gt; bytes at &lt;code&gt;arr-&amp;gt;data&lt;/code&gt;, remains allocated. No pointer to it exists anymore (the struct that held the pointer is freed), so the memory is &lt;em&gt;leaked&lt;/em&gt;. It will never be freed for the rest of the program's lifetime. On a long-running program, repeated leaks like this accumulate and eventually exhaust memory. Valgrind would report "definitely lost: N bytes in 1 blocks."&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Our array works, but it has a crippling limitation: when it's full, push fails. The user has to guess the right capacity upfront, and if they guess wrong, they're stuck.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;&lt;a href="https://pablogs.dev/posts/post-02-growing-pain/index.md" rel="noopener noreferrer"&gt;Post 2&lt;/a&gt;: "Growing Pains: realloc and Automatic Capacity Management"&lt;/strong&gt;, we remove this limitation. We'll introduce &lt;code&gt;realloc&lt;/code&gt;, the call that says "give me more space, and copy my data to the new location if needed." You'll learn why old pointers become invalid after a realloc, why the growth factor matters (spoiler: it determines your amortized cost), and why you must &lt;em&gt;never&lt;/em&gt; write &lt;code&gt;arr-&amp;gt;data = realloc(arr-&amp;gt;data, new_size)&lt;/code&gt; directly.&lt;/p&gt;

&lt;p&gt;The fixed-capacity array you built today is the foundation. Everything from here builds on it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/ansuzgs/dynamic-arrays-c/blob/main/src/post_01.c" rel="noopener noreferrer"&gt;Full source code&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>c</category>
      <category>computerscience</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
