{"article":{"slug":"arena-allocators-in-c","title":"Arena Allocators in C","subtitle":null,"summary":"A practical introduction to arena allocators in C: what they are, why grouping allocations into one region makes memory management faster and less leak-prone, and a step-by-step personal implementation with code, plus the trade-offs of using them.","content_type":"tutorial","language":"en","canonical_url":"https://eliasebner.com/blog/guides/arena-allocators-in-c/","author":{"name":"Elias Ebner","url":null,"person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"eliasebner.com","url":"https://eliasebner.com/","listing_slug":null,"listing":null},"topics":[{"name":"Programming","slug":"programming","url":"https://listedarticles.com/topics/programming"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":2184,"reading_minutes":9,"published_at":"2026-10-11T00:00:00.000Z","added_at":"2026-10-11T17:12:49.696Z","updated_at":"2026-10-11T17:12:49.696Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/arena-allocators-in-c","markdown_url":"https://listedarticles.com/articles/arena-allocators-in-c.md","example":false,"citation":"Elias Ebner, eliasebner.com. \"Arena Allocators in C.\" 11 Oct 2026. https://eliasebner.com/blog/guides/arena-allocators-in-c/ (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://eliasebner.com/blog/guides/arena-allocators-in-c/"},"body_markdown":"Oct 11, 2026\n\n# Arena Allocators in C\n\n---\n\nAs we all know, C is a very low level language.\nIt allows you (and kind of forces you) to manage the memory you use by yourself.\nThis is one of the (probably many) reasons that people are turn to other languages when\nit comes to their daily programming.\nIn this article, I will show you a paradigm for managing memory which is not only very\nperformant, but it’s also extremely versatile. It makes the task significantly easier\nand decrease the chance of memory leaks by a large margin.\n\nMaybe you already know about them, maybe not, in any case: I’m talking about arena\nallocators.\n\nI will get to the implementation, but before I do that, here is a brief introduction\nto the concept of an arena allocator.\n\n## What Are Arena Allocators?\n\nThe way people usually go about managing memory is that they allocate\na small amount of memory every time they need it from the heap with `malloc` - just what they need.\nIf they find out later that they need more than what was originally planned, they use `realloc`\nto increase the size of their memory allocation.\n\nOnce they are done with using that memory, they call `free` to tell the operating system that this\nmemory is no longer needed and it can be employed for other tasks.\n\nThis is great, but it has a few issues:\n\n**1.**\n\nThe programmer must remember to free the memory once he is done using it. This is especially dangerous\nif the allocation happens in a loop - like in games, for example - since in that case the operating system\nkeeps allocating more and more memory, but that memory is never freed.\n\nMemory, as a resource, is not infinite. At some point, your computer will run out, and your program\nwill be terminated by the operating system (i.e. it will crash).\n\n**2.**\n\nAllocating and freeing many small chunks of memory is *slow*.\n\n**3.**\n\nIf you have a function like:\n\n```\ni32 *create_array(usize len) {\n    i32 *elements = (i32 *)malloc(sizeof(int));\n    return elements;\n}\n```\n\nNow, tell me: how does the programmer calling the function know that this\nfunction performs a memory allocation? In this case, one could figure it out by\nlooking at the name of the function and thinking about what the function could\npossibly be doing, but it’s not always so trivial.\n\nRemember, this is important, since the programmer needs to know when to free the memory.\n\nThe programmer needs to be made aware of the fact that it is his responsibility to free\nthe function.\n\nThere are probably more issues that I have not thought about, but these are some of\nthe main ones I could think about on the spot.\n\n**The Solution.**\n\nEnter *the arena allocator*.\n\nMost lifetimes (i.e. how long a certain region of memory is used before it is freed)\ncan be grouped into one of a handful of categories:\n\n1. The memory lives for the duration of the entire program\n2. The memory lives until the completion of some tasks (i.e. a frame in a game or the handling of an HTTP request)\n3. The memory lives for the scope of a function\n\nThis is usually enough grouping, but depending on what you consider a group, there may be more.\nIn any case, we can just allocate a large chunk that we will use to work with things that have a similar\nlifetime (i.e. things that can be freed together).\n\nWe then have a large amount of memory that can be used. Whenever we use some of that memory to do something,\nwe advance a pointer to remember up to which point the memory has been used and where the free memory begins.\n\nThis will become clearer once you see the implementation.\n\nWhen we don’t need any of the data that lives inside that large chunk of memory anymore, we can simply\nfree it all at once (or - if we want to reuse that same memory - reinitialize the memory arena to\nits initial state and begin allocating from the start again).\n\nThis saves individial memory allocations (which remember, are slow), and makes us have to worry less about\nmanaging memory since we only have to worry about a couple of different “chunks” of memory instead of small\npieces used for individual arrays.\n\n## The Implementation\n\n### Clarification\n\nBefore we start, I almost always include this file at the top of my code when I use C.\n\nIt simply `typedef`s some built-in types and has some useful, commonly used utilities.\nI include `.c` files directly, since I use unity builds for faster compilation.\n\nYou can easily write the header files for this if you prefer that.\n\n```\n// types.c\n#ifndef TYPES_C\n#define TYPES_C\n\n#include <stddef.h>\n#include <stdint.h>\n\ntypedef uint8_t u8;\ntypedef uint16_t u16;\ntypedef uint32_t u32;\ntypedef uint64_t u64;\n\ntypedef int8_t i8;\ntypedef int16_t i16;\ntypedef int32_t i32;\ntypedef int64_t i64;\n\ntypedef float f32;\ntypedef double f64;\n\ntypedef size_t usize;\n\n#define KiB(x) x * 1024\n#define MiB(x) KiB(x) * 1024\n#define GiB(x) MiB(x) * 1024\n\n#define MIN(a, b) a < b ? a : b\n#define MAX(a, b) a > b ? a : b\n\n#endif // TYPES_C\n```\n\n### `arena.c`\n\nHere is the actual arena implementation.\n\nWe start with the include guard (again, because of the unity build), and include\nsome `libc` headers we will use:\n\n```\n#ifndef ARENA_C\n#define ARENA_C\n\n#include \"types.c\"\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n\n// code goes here\n\n#endif // ARENA_C\n```\n\nHere is the arena struct definition:\n\n```\nstruct arena {\n    void *data;\n    usize size;\n    usize used;\n};\n```\n\nIt keeps track of how much memory was used. This could also be achieved by using a pointer\nthat points to the start of the region in memory that is still unused.\n\nThe `size` field is needed to understand if we are overflowing the arena when allocating\n(that could very well happen if you are using too much memory).\n\nAnd `data` simply points to the start of the arena.\n\nSo far so good, but now we need a way to create a new arena. Here it is:\n\n```\nstruct arena_create_out {\n    struct arena arena;\n    bool success;\n};\n[[nodiscard]]\nstruct arena_create_out arena_create(usize size) {\n    struct arena_create_out out = {0};\n    out.arena.data = malloc(size);\n    if (out.arena.data == NULL)\n        return out;\n    out.arena.size = size;\n    out.arena.used = 0;\n    out.success = true;\n    return out;\n}\n```\n\nWhen returning multiple values, instead of using output parameters, which I find\nconfusing and it makes the API unintuitive in my opinion, I much prefer to return\nstructs.\n\nWe then need a way to allocate some memory in this arena.\nBefore this, define this macro (I have it at the top of the file, but I am\nintroducing it now since this is when it becomes relevant):\n\n```\n#define ARENA_ALIGN 8\n```\n\nThis will be used to align the data.\nFor some reason, our computers are much better at dealing with data that is aligned\nat 8 bytes. This means that the start of each new allocation has to be at a multiple of 8 bytes.\n\n`malloc` handles this for us, but since we are not allocating with `malloc` now, but\nsimply reserving some space in an already allocated chunk of memory, we have\nto align the data ourselves to make for faster accessing.\n\nHere is the allocation function:\n\n```\nstruct arena_alloc_out {\n    void *ptr;\n    bool success;\n};\n[[nodiscard]]\nstruct arena_alloc_out arena_alloc(struct arena *arena, usize size) {\n    struct arena_alloc_out out = {0};\n    usize alignment = (ARENA_ALIGN - (arena->used % ARENA_ALIGN)) % ARENA_ALIGN;\n    out.ptr = arena->data + arena->used + alignment;\n    if (out.ptr + size > arena->data + arena->size)\n        return out;\n    arena->used += size + alignment;\n    out.success = true;\n    return out;\n}\n```\n\nHere, we understand how much “padding” we need to add (the `alignment` variable)\nat the end of our allocation such that the next one will be aligned to 8 bytes.\n\nI use the pattern of having a `success` field in the return type of the function,\nand on error I simply return early (since it is initialized as all-zeroes with\n`= {0}`, `success` will be `false`).\n\nIf you don’t like this approach, it should be fairly trivial to migrate\nthe API to a different convention.\n\nMost of the work is done, I created a simple wrapper to change the size of\nan allocation. It simply allocates at the end of the arena with the new size\nand copies the data over. The old data is left untouched as it will be\nfreed once the arena is freed (remember, we cannot free individual allocations,\nit’s either the whole arena or nothing).\n\nYou don’t *really* need it, but I found that it made my code cleaner, so here it is:\n\n```\nstruct arena_realloc_out {\n    void *ptr;\n    bool success;\n};\n[[nodiscard]]\nstruct arena_realloc_out arena_realloc(struct arena *arena, usize new_size, void *src, usize old_size) {\n    struct arena_realloc_out out = {0};\n    struct arena_alloc_out alloc_out = arena_alloc(arena, new_size);\n    if (!alloc_out.success)\n        return out;\n\n    out.ptr = memcpy(alloc_out.ptr, src, MIN(old_size, new_size));\n    out.success = true;\n    return out;\n}\n```\n\nIf the new size is smaller than the new size, then a part of the data is cut off at the end.\nAlso, internally `realloc` knows the size of each allocation associated with each pointer,\nso you do not need to pass the old size, only the new size.\n\nSince our arena implementation does not keep track of the size of allocations in any way,\nwe will have to pass the old size to the function separately. The user of the arena\nis expected to know the size of each allocation.\n\nThis could also be implemented, but I would like to keep it simple.\n\nThe next two functions are small wrappers, they’re only there for convenience but are not\nessential:\n\n```\nvoid arena_clear(struct arena *arena) { arena->used = 0; }\n\nvoid arena_free(struct arena *arena) { free(arena->data); }\n```\n\nThis is it for my implementation.\n\n## Example Usage\n\nI have a simple string library that I have implemented for my own use cases.\nIt has two concepts: a string view and an owned string.\n\nAn owned string is NUL-terminated and `owns` its data (i.e. it has to be\nallocated and freed). That also means that one can modify an owned string directly.\n\nA string view only *views* the data, so you cannot modify it directly, since there may exist\nmultiple string views viewing the same data and modifying it would lead to unexpected changes\nto the other strings. On the other hand, only one owned string shall exists for any given region\nof data.\n\nWhat is important is that I need a way of converting string views to owned strings.\nTo do this, we have to create a new owned string (which means allocating the memory for it),\nand copy the data over. Again, this is to make sure that changes to this owned string\ndo not interfere with other potential string views viewing it.\n\nWe also add a NUL-terminator at the end of the owned string (to make it compatible\nwith other legacy C APIs).\n\nTo do this, we pass an arena allocator to the function and allocate to that arena:\n\n```\nstruct str_view {\n    const u8 *data;\n    usize len;\n};\n\nstruct str_owned {\n    u8 *data;\n    usize len;\n};\n\nstruct str_view_to_str_owned_out {\n    struct str_owned str;\n    bool success;\n};\n[[nodiscard]]\nstruct str_view_to_str_owned_out str_view_to_str_owned(\n    struct arena *arena,\n    struct str_view s\n) {\n    struct str_view_to_str_owned_out out = {0};\n    struct arena_alloc_out alloc_out = arena_alloc(arena, s.len + 1);\n    if (!alloc_out.success)\n        return out;\n    out.str.data = alloc_out.ptr;\n    memcpy(out.str.data, s.data, s.len);\n    out.str.len = s.len;\n    out.str.data[out.str.len] = 0;\n    out.success = true;\n    return out;\n}\n```\n\nThis does two things:\n\n1. The caller immediately knows that this function allocates memory (since its first argument is an arena pointer)\n2. The caller can decide where (i.e. with which lifetime) to allocate the memory.\n\nRegarding point no. 2, since the caller decides the lifetime by passing the relevant arena,\nthey do not have to worry about freeing the memory directly,\nthe memory is simply freed once the lifetime is over and the arena is discarded (or cleared).\nThis could be at the end of the program (in which case freeing the memory would be unnecessary,\nsince the operating system handles it for us), or at the end of the function,\nor whenever they are sure that the memory is not needed anymore.\n\n## Conclusion\n\nI recently started including arenas in my programming habits and I must say that I have been really satisfied.\nI have migrated my string library to use arenas and I think you could benefit a lot from using arenas as well.\n\nSometimes the gains in developer experience are larger than in other cases, but I would say that having this\ntool under your belt as a programmer is definitely a net benefit.\n\nI don’t claim to be an expert and this implementation is just something that I have built\nmyself for personal use. It may not be perfect, but it should serve as a nice introduction to get you started.\n\nThanks for reading and I hope you found this useful!","body_html":"<p>Oct 11, 2026</p>\n<h1 id=\"arena-allocators-in-c\">Arena Allocators in C</h1>\n<hr />\n<p>As we all know, C is a very low level language.\nIt allows you (and kind of forces you) to manage the memory you use by yourself.\nThis is one of the (probably many) reasons that people are turn to other languages when\nit comes to their daily programming.\nIn this article, I will show you a paradigm for managing memory which is not only very\nperformant, but it’s also extremely versatile. It makes the task significantly easier\nand decrease the chance of memory leaks by a large margin.</p>\n<p>Maybe you already know about them, maybe not, in any case: I’m talking about arena\nallocators.</p>\n<p>I will get to the implementation, but before I do that, here is a brief introduction\nto the concept of an arena allocator.</p>\n<h2 id=\"what-are-arena-allocators\">What Are Arena Allocators?</h2>\n<p>The way people usually go about managing memory is that they allocate\na small amount of memory every time they need it from the heap with <code>malloc</code> - just what they need.\nIf they find out later that they need more than what was originally planned, they use <code>realloc</code>\nto increase the size of their memory allocation.</p>\n<p>Once they are done with using that memory, they call <code>free</code> to tell the operating system that this\nmemory is no longer needed and it can be employed for other tasks.</p>\n<p>This is great, but it has a few issues:</p>\n<p><strong>1.</strong></p>\n<p>The programmer must remember to free the memory once he is done using it. This is especially dangerous\nif the allocation happens in a loop - like in games, for example - since in that case the operating system\nkeeps allocating more and more memory, but that memory is never freed.</p>\n<p>Memory, as a resource, is not infinite. At some point, your computer will run out, and your program\nwill be terminated by the operating system (i.e. it will crash).</p>\n<p><strong>2.</strong></p>\n<p>Allocating and freeing many small chunks of memory is <em>slow</em>.</p>\n<p><strong>3.</strong></p>\n<p>If you have a function like:</p>\n<pre><code>i32 *create_array(usize len) {\n    i32 *elements = (i32 *)malloc(sizeof(int));\n    return elements;\n}</code></pre>\n<p>Now, tell me: how does the programmer calling the function know that this\nfunction performs a memory allocation? In this case, one could figure it out by\nlooking at the name of the function and thinking about what the function could\npossibly be doing, but it’s not always so trivial.</p>\n<p>Remember, this is important, since the programmer needs to know when to free the memory.</p>\n<p>The programmer needs to be made aware of the fact that it is his responsibility to free\nthe function.</p>\n<p>There are probably more issues that I have not thought about, but these are some of\nthe main ones I could think about on the spot.</p>\n<p><strong>The Solution.</strong></p>\n<p>Enter <em>the arena allocator</em>.</p>\n<p>Most lifetimes (i.e. how long a certain region of memory is used before it is freed)\ncan be grouped into one of a handful of categories:</p>\n<ol><li>The memory lives for the duration of the entire program</li><li>The memory lives until the completion of some tasks (i.e. a frame in a game or the handling of an HTTP request)</li><li>The memory lives for the scope of a function</li></ol>\n<p>This is usually enough grouping, but depending on what you consider a group, there may be more.\nIn any case, we can just allocate a large chunk that we will use to work with things that have a similar\nlifetime (i.e. things that can be freed together).</p>\n<p>We then have a large amount of memory that can be used. Whenever we use some of that memory to do something,\nwe advance a pointer to remember up to which point the memory has been used and where the free memory begins.</p>\n<p>This will become clearer once you see the implementation.</p>\n<p>When we don’t need any of the data that lives inside that large chunk of memory anymore, we can simply\nfree it all at once (or - if we want to reuse that same memory - reinitialize the memory arena to\nits initial state and begin allocating from the start again).</p>\n<p>This saves individial memory allocations (which remember, are slow), and makes us have to worry less about\nmanaging memory since we only have to worry about a couple of different “chunks” of memory instead of small\npieces used for individual arrays.</p>\n<h2 id=\"the-implementation\">The Implementation</h2>\n<h3 id=\"clarification\">Clarification</h3>\n<p>Before we start, I almost always include this file at the top of my code when I use C.</p>\n<p>It simply <code>typedef</code>s some built-in types and has some useful, commonly used utilities.\nI include <code>.c</code> files directly, since I use unity builds for faster compilation.</p>\n<p>You can easily write the header files for this if you prefer that.</p>\n<pre><code>// types.c\n#ifndef TYPES_C\n#define TYPES_C\n\n#include &lt;stddef.h&gt;\n#include &lt;stdint.h&gt;\n\ntypedef uint8_t u8;\ntypedef uint16_t u16;\ntypedef uint32_t u32;\ntypedef uint64_t u64;\n\ntypedef int8_t i8;\ntypedef int16_t i16;\ntypedef int32_t i32;\ntypedef int64_t i64;\n\ntypedef float f32;\ntypedef double f64;\n\ntypedef size_t usize;\n\n#define KiB(x) x * 1024\n#define MiB(x) KiB(x) * 1024\n#define GiB(x) MiB(x) * 1024\n\n#define MIN(a, b) a &lt; b ? a : b\n#define MAX(a, b) a &gt; b ? a : b\n\n#endif // TYPES_C</code></pre>\n<h3 id=\"arena-c\"><code>arena.c</code></h3>\n<p>Here is the actual arena implementation.</p>\n<p>We start with the include guard (again, because of the unity build), and include\nsome <code>libc</code> headers we will use:</p>\n<pre><code>#ifndef ARENA_C\n#define ARENA_C\n\n#include &quot;types.c&quot;\n#include &lt;stdbool.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;string.h&gt;\n\n// code goes here\n\n#endif // ARENA_C</code></pre>\n<p>Here is the arena struct definition:</p>\n<pre><code>struct arena {\n    void *data;\n    usize size;\n    usize used;\n};</code></pre>\n<p>It keeps track of how much memory was used. This could also be achieved by using a pointer\nthat points to the start of the region in memory that is still unused.</p>\n<p>The <code>size</code> field is needed to understand if we are overflowing the arena when allocating\n(that could very well happen if you are using too much memory).</p>\n<p>And <code>data</code> simply points to the start of the arena.</p>\n<p>So far so good, but now we need a way to create a new arena. Here it is:</p>\n<pre><code>struct arena_create_out {\n    struct arena arena;\n    bool success;\n};\n[[nodiscard]]\nstruct arena_create_out arena_create(usize size) {\n    struct arena_create_out out = {0};\n    out.arena.data = malloc(size);\n    if (out.arena.data == NULL)\n        return out;\n    out.arena.size = size;\n    out.arena.used = 0;\n    out.success = true;\n    return out;\n}</code></pre>\n<p>When returning multiple values, instead of using output parameters, which I find\nconfusing and it makes the API unintuitive in my opinion, I much prefer to return\nstructs.</p>\n<p>We then need a way to allocate some memory in this arena.\nBefore this, define this macro (I have it at the top of the file, but I am\nintroducing it now since this is when it becomes relevant):</p>\n<pre><code>#define ARENA_ALIGN 8</code></pre>\n<p>This will be used to align the data.\nFor some reason, our computers are much better at dealing with data that is aligned\nat 8 bytes. This means that the start of each new allocation has to be at a multiple of 8 bytes.</p>\n<p><code>malloc</code> handles this for us, but since we are not allocating with <code>malloc</code> now, but\nsimply reserving some space in an already allocated chunk of memory, we have\nto align the data ourselves to make for faster accessing.</p>\n<p>Here is the allocation function:</p>\n<pre><code>struct arena_alloc_out {\n    void *ptr;\n    bool success;\n};\n[[nodiscard]]\nstruct arena_alloc_out arena_alloc(struct arena *arena, usize size) {\n    struct arena_alloc_out out = {0};\n    usize alignment = (ARENA_ALIGN - (arena-&gt;used % ARENA_ALIGN)) % ARENA_ALIGN;\n    out.ptr = arena-&gt;data + arena-&gt;used + alignment;\n    if (out.ptr + size &gt; arena-&gt;data + arena-&gt;size)\n        return out;\n    arena-&gt;used += size + alignment;\n    out.success = true;\n    return out;\n}</code></pre>\n<p>Here, we understand how much “padding” we need to add (the <code>alignment</code> variable)\nat the end of our allocation such that the next one will be aligned to 8 bytes.</p>\n<p>I use the pattern of having a <code>success</code> field in the return type of the function,\nand on error I simply return early (since it is initialized as all-zeroes with\n<code>= {0}</code>, <code>success</code> will be <code>false</code>).</p>\n<p>If you don’t like this approach, it should be fairly trivial to migrate\nthe API to a different convention.</p>\n<p>Most of the work is done, I created a simple wrapper to change the size of\nan allocation. It simply allocates at the end of the arena with the new size\nand copies the data over. The old data is left untouched as it will be\nfreed once the arena is freed (remember, we cannot free individual allocations,\nit’s either the whole arena or nothing).</p>\n<p>You don’t <em>really</em> need it, but I found that it made my code cleaner, so here it is:</p>\n<pre><code>struct arena_realloc_out {\n    void *ptr;\n    bool success;\n};\n[[nodiscard]]\nstruct arena_realloc_out arena_realloc(struct arena *arena, usize new_size, void *src, usize old_size) {\n    struct arena_realloc_out out = {0};\n    struct arena_alloc_out alloc_out = arena_alloc(arena, new_size);\n    if (!alloc_out.success)\n        return out;\n\n    out.ptr = memcpy(alloc_out.ptr, src, MIN(old_size, new_size));\n    out.success = true;\n    return out;\n}</code></pre>\n<p>If the new size is smaller than the new size, then a part of the data is cut off at the end.\nAlso, internally <code>realloc</code> knows the size of each allocation associated with each pointer,\nso you do not need to pass the old size, only the new size.</p>\n<p>Since our arena implementation does not keep track of the size of allocations in any way,\nwe will have to pass the old size to the function separately. The user of the arena\nis expected to know the size of each allocation.</p>\n<p>This could also be implemented, but I would like to keep it simple.</p>\n<p>The next two functions are small wrappers, they’re only there for convenience but are not\nessential:</p>\n<pre><code>void arena_clear(struct arena *arena) { arena-&gt;used = 0; }\n\nvoid arena_free(struct arena *arena) { free(arena-&gt;data); }</code></pre>\n<p>This is it for my implementation.</p>\n<h2 id=\"example-usage\">Example Usage</h2>\n<p>I have a simple string library that I have implemented for my own use cases.\nIt has two concepts: a string view and an owned string.</p>\n<p>An owned string is NUL-terminated and <code>owns</code> its data (i.e. it has to be\nallocated and freed). That also means that one can modify an owned string directly.</p>\n<p>A string view only <em>views</em> the data, so you cannot modify it directly, since there may exist\nmultiple string views viewing the same data and modifying it would lead to unexpected changes\nto the other strings. On the other hand, only one owned string shall exists for any given region\nof data.</p>\n<p>What is important is that I need a way of converting string views to owned strings.\nTo do this, we have to create a new owned string (which means allocating the memory for it),\nand copy the data over. Again, this is to make sure that changes to this owned string\ndo not interfere with other potential string views viewing it.</p>\n<p>We also add a NUL-terminator at the end of the owned string (to make it compatible\nwith other legacy C APIs).</p>\n<p>To do this, we pass an arena allocator to the function and allocate to that arena:</p>\n<pre><code>struct str_view {\n    const u8 *data;\n    usize len;\n};\n\nstruct str_owned {\n    u8 *data;\n    usize len;\n};\n\nstruct str_view_to_str_owned_out {\n    struct str_owned str;\n    bool success;\n};\n[[nodiscard]]\nstruct str_view_to_str_owned_out str_view_to_str_owned(\n    struct arena *arena,\n    struct str_view s\n) {\n    struct str_view_to_str_owned_out out = {0};\n    struct arena_alloc_out alloc_out = arena_alloc(arena, s.len + 1);\n    if (!alloc_out.success)\n        return out;\n    out.str.data = alloc_out.ptr;\n    memcpy(out.str.data, s.data, s.len);\n    out.str.len = s.len;\n    out.str.data[out.str.len] = 0;\n    out.success = true;\n    return out;\n}</code></pre>\n<p>This does two things:</p>\n<ol><li>The caller immediately knows that this function allocates memory (since its first argument is an arena pointer)</li><li>The caller can decide where (i.e. with which lifetime) to allocate the memory.</li></ol>\n<p>Regarding point no. 2, since the caller decides the lifetime by passing the relevant arena,\nthey do not have to worry about freeing the memory directly,\nthe memory is simply freed once the lifetime is over and the arena is discarded (or cleared).\nThis could be at the end of the program (in which case freeing the memory would be unnecessary,\nsince the operating system handles it for us), or at the end of the function,\nor whenever they are sure that the memory is not needed anymore.</p>\n<h2 id=\"conclusion\">Conclusion</h2>\n<p>I recently started including arenas in my programming habits and I must say that I have been really satisfied.\nI have migrated my string library to use arenas and I think you could benefit a lot from using arenas as well.</p>\n<p>Sometimes the gains in developer experience are larger than in other cases, but I would say that having this\ntool under your belt as a programmer is definitely a net benefit.</p>\n<p>I don’t claim to be an expert and this implementation is just something that I have built\nmyself for personal use. It may not be perfect, but it should serve as a nice introduction to get you started.</p>\n<p>Thanks for reading and I hope you found this useful!</p>","headings":[{"level":1,"text":"Arena Allocators in C","id":"arena-allocators-in-c"},{"level":2,"text":"What Are Arena Allocators?","id":"what-are-arena-allocators"},{"level":2,"text":"The Implementation","id":"the-implementation"},{"level":3,"text":"Clarification","id":"clarification"},{"level":3,"text":"arena.c","id":"arena-c"},{"level":2,"text":"Example Usage","id":"example-usage"},{"level":2,"text":"Conclusion","id":"conclusion"}]}}