Standar C (§6.2.4) mengelompokkan variabel menjadi tiga storage duration: automatic, static, dan allocated. Pemilihan kategori menentukan di mana variabel disimpan (stack, data/bss, atau heap), kapan diciptakan, kapan dihancurkan, dan apa konsekuensinya bagi pointer yang menunjuk ke variabel tersebut. Banyak bug memori di C berakar pada kebingungan antara ketiganya—misalnya mengembalikan alamat variabel automatic, atau lupa memanggil free untuk allocated storage. Pada chapter ini kita akan melihat secara konkret bagaimana ketiga kategori itu berperilaku berbeda dalam program yang sama, dan kapan kita memilih satu di atas yang lain.

Karakteristik Tiga Storage Duration #

Ringkasan karakteristiknya:

Storage DurationKeywordLokasiAlokasiDealokasi
Automatic(default untuk variabel lokal)StackSaat block dimasukiSaat block keluar
Staticstatic, atau global biasadata/bssSaat program dimulaiSaat program berakhir
Allocatedmalloc/calloc/reallocHeapSaat fungsi alokator dipanggilSaat free dipanggil

Mekanisme kerja di balik layar: untuk automatic storage, compiler menggeser stack pointer di awal block dan mengembalikannya di akhir block—tidak ada panggilan runtime. Untuk static storage, linker mengalokasikan slot tetap di segmen data (terinisialisasi) atau bss (tak terinisialisasi) dan alamatnya tidak pernah berubah. Untuk allocated storage, allocator glibc mengelola heap dan mengembalikan pointer yang valid selama blok belum di-free.

Ketiga Storage dalam Satu Program #

Kita akan memverifikasi teori di atas dengan program yang menggunakan ketiga kategori sekaligus. Alamat yang dicetak akan menunjukkan di mana tiap variabel berada di virtual address space.

c
#include <stdio.h>
#include <stdlib.h>

/* static storage duration: global */
int global_counter = 100;

void demo_block(void) {
    int auto_x = 5;            /* automatic storage di stack */
    printf("  auto_x = %d (alamat %p)\n", auto_x, (void*)&auto_x);

    {
        int auto_y = 7;        /* automatic storage, lifetime hanya block ini */
        printf("  auto_y = %d (alamat %p)\n", auto_y, (void*)&auto_y);
    }
    /* auto_y sudah tidak valid di sini */
}

int main(void) {
    printf("global_counter = %d (alamat %p)\n", global_counter, (void*)&global_counter);

    int *heap_ptr = malloc(sizeof(int));
    if (heap_ptr) {
        *heap_ptr = 42;
        printf("heap_ptr -> %d (alamat %p)\n", *heap_ptr, (void*)heap_ptr);
        free(heap_ptr);
    }

    printf("Memanggil demo_block:\n");
    demo_block();

    return 0;
}

Output yang Dihasilkan:

text
global_counter = 100 (alamat 0x564bf86b9030)
heap_ptr -> 42 (alamat 0x564bfa1492b0)
Memanggil demo_block:
  auto_x = 5 (alamat 0x7ffd5a6a6c8c)
  auto_y = 7 (alamat 0x7ffd5a6a6c88)

Alamat global_counter (0x564...) berada di wilayah data segmen bersama executable; alamat heap_ptr (0x564...) di wilayah heap yang sedikit lebih tinggi; alamat auto_x dan auto_y (0x7ffd...) jauh di atas di wilayah stack. Perhatikan juga bahwa auto_x dan auto_y hanya berbeda 4 byte (...6c8c vs ...6c88), keduanya berada di stack frame yang sama.

Mari kita telaah baris-baris kode di atas:

  1. Baris 5 (int global_counter = 100;): variabel global terinisialisasi—static storage duration. Linker menempatkannya di segmen data, dan nilainya 100 diisi saat program dimuat dari berkas executable.
  2. Baris 8 (int auto_x = 5;): variabel lokal biasa—automatic storage duration. Memori berada di stack frame fungsi demo_block. Saat fungsi kembali, memori ini dibebaskan.
  3. Baris 12 (int auto_y = 7;): variabel lokal di dalam inner block. Lifetime-nya hanya selama inner block aktif. Setelah inner block keluar (baris 14), auto_y tidak boleh diakses lagi.
  4. Baris 19 (int *heap_ptr = malloc(sizeof(int));): alokasi allocated storage. Memori berada di heap dan tetap valid sampai free(heap_ptr) dipanggil pada baris 22.
  5. Baris 22 (free(heap_ptr);): dealokasi manual. Setelah baris ini, heap_ptr menjadi dangling—topik yang akan kita perdalam di chapter Use After Free.

Pola Umum: Static Lookup Table #

Salah satu kegunaan static storage yang sering ditemui adalah lookup table di dalam fungsi. Karena array ini hanya diinisialisasi sekali dan tidak pernah berubah, static storage lebih efisien daripada membuat ulang array di stack setiap pemanggilan.

c
#include <stdio.h>

const char *nama_hari(int idx) {
    /* static storage duration: array string literal di rodata */
    static const char *hari[] = {
        "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu"
    };
    if (idx < 0 || idx > 6) return "Tidak valid";
    return hari[idx];
}

int main(void) {
    for (int i = 0; i < 7; i++) {
        printf("Hari %d: %s\n", i + 1, nama_hari(i));
    }
    printf("Hari ke-9: %s\n", nama_hari(8));
    return 0;
}

Output yang Dihasilkan:

text
Hari 1: Senin
Hari 2: Selasa
Hari 3: Rabu
Hari 4: Kamis
Hari 5: Jumat
Hari 6: Sabtu
Hari 7: Minggu
Hari ke-9: Tidak valid

Keyword static pada static const char *hari[] membuat array ini hanya diinisialisasi sekali dan dialokasikan di segmen data/rodata—bukan diciptakan ulang di stack setiap kali nama_hari dipanggil. String literal "Senin", "Selasa", dan seterusnya sendiri berada di rodata sebagai static storage duration.