Passing a pointer slot can hand heap ownership to a cleanup helper.

owner pointer One pointer variable is responsible for freeing heap storage.
clear after free Setting the caller's pointer to `0` after `free` prevents accidental reuse.

Ownership Transfer

value
ownership_transfer.c
Replay: real traced execution (multi-file project)
#include <stdio.h>
#include <stdlib.h>

static void release(int **owner) {
    free(*owner);
    *owner = 0;
}

int main(void) {
    int value = 6;
    int *owned = (int *)malloc(sizeof(int));
    *owned = value;
    int before = *owned;

    release(&owned);

    printf("before=%d released=%d\n", before, owned == 0);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>

static void release(int **owner) {
    free(*owner);
    *owner = 0;
}

int main(void) {
    int value = 9;
    int *owned = (int *)malloc(sizeof(int));
    *owned = value;
    int before = *owned;

    release(&owned);

    printf("before=%d released=%d\n", before, owned == 0);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>

static void release(int **owner) {
    free(*owner);
    *owner = 0;
}

int main(void) {
    int value = 12;
    int *owned = (int *)malloc(sizeof(int));
    *owned = value;
    int before = *owned;

    release(&owned);

    printf("before=%d released=%d\n", before, owned == 0);
    return 0;
}
  1. value ← 6, owned ← ⟨addr A⟩, before ← 6

    9int main(void) {10    int value→ 6 = 6; //@value=9, 1211    int *owned→ ⟨addr A⟩ = (int *)malloc(sizeof(int));12    *owned⟨addr A⟩ = value6;13    int before→ 6 = *owned⟨addr A⟩;1415    release(&owned⟨addr A⟩);
  2. static void release(int **owner)

    4static void release(int **owner⟨addr B⟩) {5    free(*owner⟨addr B⟩);6    *owner⟨addr B⟩ = 0;7}
  3. owned ← 0

    15    release(&owned→ 0);1617    printf("before=%d released=%d\n", before6, owned0 == 0);18    return 0;19}
    outputbefore=6 released=1
  1. value ← 9, owned ← ⟨addr A⟩, before ← 9

    9int main(void) {10    int value→ 9 = 9;11    int *owned→ ⟨addr A⟩ = (int *)malloc(sizeof(int));12    *owned⟨addr A⟩ = value9;13    int before→ 9 = *owned⟨addr A⟩;1415    release(&owned⟨addr A⟩);
  2. static void release(int **owner)

    4static void release(int **owner⟨addr B⟩) {5    free(*owner⟨addr B⟩);6    *owner⟨addr B⟩ = 0;7}
  3. owned ← 0

    15    release(&owned→ 0);1617    printf("before=%d released=%d\n", before9, owned0 == 0);18    return 0;19}
    outputbefore=9 released=1
  1. value ← 12, owned ← ⟨addr A⟩, before ← 12

    9int main(void) {10    int value→ 12 = 12;11    int *owned→ ⟨addr A⟩ = (int *)malloc(sizeof(int));12    *owned⟨addr A⟩ = value12;13    int before→ 12 = *owned⟨addr A⟩;1415    release(&owned⟨addr A⟩);
  2. static void release(int **owner)

    4static void release(int **owner⟨addr B⟩) {5    free(*owner⟨addr B⟩);6    *owner⟨addr B⟩ = 0;7}
  3. owned ← 0

    15    release(&owned→ 0);1617    printf("before=%d released=%d\n", before12, owned0 == 0);18    return 0;19}
    outputbefore=12 released=1

Exercise: ownership_transfer.c

Move heap ownership into a cleanup helper and prove the caller cannot free the same pointer twice