Skip to content
📖 Auto vs Modern CBook contents

Chapter 13: Storage ​

Level 2 — Cognition

malloc, storage duration, initialization -- how C manages object lifetimes and how Auto makes it automatic.

Memory management is where C programs most often go wrong. Memory leaks, use-after-free, double-free, and dangling pointers account for a large fraction of C's security vulnerabilities. This chapter examines C's storage mechanisms and shows how Auto eliminates entire categories of memory errors.


13.1 malloc and Friends ​

C provides four functions for dynamic memory allocation:

c
// C Deep Dive: dynamic allocation
void *malloc(size_t size);        // allocate size bytes (uninitialized)
void *calloc(size_t n, size_t s); // allocate n*s bytes (zero-initialized)
void *realloc(void *p, size_t s); // resize allocation (may move)
void free(void *p);               // deallocate

Typical usage in C:

c
// C Deep Dive: malloc lifecycle
int *arr = malloc(10 * sizeof(int));  // allocate
if (!arr) { /* handle error */ }       // check NULL
for (int i = 0; i < 10; i++) {
    arr[i] = i * i;                    // use
}
// ... later ...
free(arr);                             // deallocate
arr = NULL;                            // avoid dangling pointer

The problems are legion:

  • Forgetting to free: memory leaks that grow over time.
  • Using after free: undefined behavior, often exploitable.
  • Double free: corrupts the heap allocator's internal state.
  • Not checking NULL: dereferencing a failed allocation crashes.
  • Wrong size: malloc(sizeof(int*)) when you meant malloc(sizeof(int)).

Auto's replacement: automatic storage. Auto manages object lifetimes automatically. There is no malloc, free, realloc, or calloc in Auto:

auto
// Auto: no malloc/free, automatic lifetime management
var arr [10]int
for i in 0..10 {
    arr[i] = i * i
}
print("First:", arr[0])
// no free needed -- storage reclaimed automatically

When Auto needs dynamically-sized collections, the standard library provides growable containers with automatic cleanup:

auto
// Auto: growable buffer
type Buffer {
    data [256]int
    size int
}

fn Buffer.new() Buffer {
    Buffer([256]int{}, 0)
}

fn Buffer.push(b Buffer, val int) {
    if b.size < 256 {
        b.data[b.size] = val
        b.size = b.size + 1
    }
}

Released under the MIT License.