Skip to content
malloc and free
step 1/5

Reading — step 1 of 5

Learn

~3 min readMemory and Structs

malloc and free

Every array so far had a size known at compile time. Real programs read n at runtime — and need n slots. Dynamic allocation is C handing you the raw storage API the garbage-collected world builds its comforts on: malloc gives you bytes, free gives them back, and nothing in between is anyone's job but yours.

The API

#include <stdlib.h>

int n;
scanf("%d", &n);

int *a = malloc(n * sizeof(int));    /* n ints' worth of bytes, from the heap */
if (a == NULL) {                     /* allocation CAN fail */
    return 1;
}

for (int i = 0; i < n; i++) {
    scanf("%d", &a[i]);              /* index it exactly like an array */
}

free(a);                             /* hand the bytes back */

Unpacking the load-bearing line: malloc takes a byte count — hence n * sizeof(int), never a bare n (that's n bytes, room for a quarter of your ints; the resulting overflow corrupts at a distance). It returns a pointer to uninitialized bytes — the garbage rule applies (calloc(n, sizeof(int)) is the zero-filling sibling). And it can return NULL when memory's exhausted; the check is one if and marks you as someone who's read a man page.

Note what just happened syntactically: a[i] works identically on malloc'd memory and stack arrays — because (pointers lesson!) indexing is pointer arithmetic. A "dynamic array" is just a pointer to enough bytes.

The contract

You now carry three obligations, each with a named failure:

  1. Free what you malloc — exactly once, when you're done. Forget → memory leak: the program's memory grows until something dies. Invisible in a 2-second exercise, fatal in a 2-month server.
  2. Don't use it after freeing — the pointer still holds the old address, but the bytes belong to someone else now. Use-after-free sometimes works, sometimes corrupts, always eventually humiliates. Defensive idiom: free(a); a = NULL; — turning any later use into an honest NULL crash.
  3. Don't free twicedouble-free corrupts the allocator's own bookkeeping. (Freeing NULL is legal and does nothing, which makes the idiom in #2 doubly protective.)

The ownership discipline that scales: for every malloc, decide at the moment you write it which line of code owns the duty to free — and in larger programs, whole APIs are shaped by that question (thing_create() / thing_destroy() pairs are C's constructors and destructors, and now you know what they're really managing). Tooling exists and is glorious — valgrind ./prog reports every leak and invalid access with line numbers — file that name away for the day you're debugging locally.

This, incidentally, is the exact problem the kernel course's allocator lessons build from first principles, and the problem Rust's borrow checker solves at compile time. One platform, three angles on the same bytes.

Your exercise: Allocate and Sum

Read n, malloc space for n ints, read them in, sum them (the accumulator rides again — long long if sums can run big), print, free. The full lifecycle in miniature:

malloc(n * sizeof(int)) → NULL-check → fill → use → free.

The grader can't see your leak — the OS reclaims everything at exit — but the exercise is graded pass/fail on output and habit-forming on purpose: type the free(a); like it matters, because the next time you allocate in a loop, it will.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…

malloc and free — C Fundamentals