Setiap variabel di C memiliki dua properti fundamental yang sering dicampuradukkan: storage duration dan lifetime. Storage duration menentukan kapan memori untuk variabel itu dialokasikan dan dibebaskan; lifetime menentukan rentang waktu selama program berjalan ketika variabel tersebut dapat diakses secara sah. Pemahaman kedua konsep ini menentukan apakah sebuah pointer valid, apakah data akan tetap ada setelah fungsi kembali, dan kapan tepatnya kita wajib memanggil free. Pada chapter ini kita akan membedakan automatic, static, dan allocated storage duration, lalu melihat bagaimana lifetime setiap kategori terbukti dalam program nyata.
Tiga Storage Duration di C #
Standar C (§6.2.4) mendefinisikan tiga storage duration utama:
- Automatic storage duration — variabel lokal biasa yang dideklarasikan di dalam block. Memori dialokasikan saat block dimasuki dan dibebaskan saat block keluar. Inilah default untuk variabel lokal non-
static. - Static storage duration — variabel global dan variabel lokal dengan keyword
static. Memori dialokasikan saat program dimulai dan dibebaskan saat program berakhir. Nilai dipertahankan antar pemanggilan fungsi. - Allocated storage duration — memori yang dipesan lewat
malloc,calloc, ataurealloc. Memori dialokasikan saat fungsi alokator dipanggil dan dibebaskan saatfreedipanggil—lifetime dikendalikan sepenuhnya oleh programmer.
Mekanisme kerja di balik layar: untuk automatic storage, compiler menghasilkan instruksi yang menggeser stack pointer saat block dimasuki—tidak ada panggilan runtime untuk alokasi. Untuk static storage, linker meletakkan variabel di segmen data (bila terinisialisasi) atau bss (bila tidak), dan alamatnya konstan sepanjang hidup program. Untuk allocated storage, allocator glibc mengelola pool heap dan mengembalikan pointer yang valid selama blok belum di-free.
Visualisasi Konsep #
Diagram berikut membandingkan lifetime tiga kategori storage duration sepanjang hidup program:
Pada diagram di atas, lifetime variabel automatic hanya berlangsung selama block tempat variabel dideklarasikan dijalankan—setiap pemanggilan fungsi menciptakan instance baru. Lifetime variabel static mencakup seluruh hidup program: alamat ditetapkan saat startup dan nilai dipertahankan antar pemanggilan fungsi. Lifetime variabel allocated dikendalikan sepenuhnya oleh programmer—dimulai saat malloc dipanggil dan berakhir saat free dipanggil, terlepas dari scope fungsi pencipta. Perbedaan lifetime inilah yang menentukan apakah sebuah pointer valid untuk dikembalikan dari fungsi.
Bukti Empiris: Static vs Automatic #
Kita bandingkan automatic dan static dengan sebuah counter yang dipanggil tiga kali dalam loop. Variabel automatic akan selalu nol di awal setiap pemanggilan, sedangkan variabel static mempertahankan nilainya.
#include <stdio.h>
void hitung_panggilan(void) {
/* automatic storage: counter_panggilan dibuang setiap kali fungsi selesai */
int counter_panggilan = 0;
counter_panggilan++;
printf("automatic: panggilan ke-%d\n", counter_panggilan);
}
void hitung_panggilan_static(void) {
/* static storage: nilai dipertahankan antar panggilan */
static int counter_panggilan = 0;
counter_panggilan++;
printf("static : panggilan ke-%d\n", counter_panggilan);
}
int main(void) {
for (int i = 0; i < 3; i++) {
hitung_panggilan();
hitung_panggilan_static();
}
return 0;
}Output yang Dihasilkan:
automatic: panggilan ke-1
static : panggilan ke-1
automatic: panggilan ke-1
static : panggilan ke-2
automatic: panggilan ke-1
static : panggilan ke-3Setiap pemanggilan hitung_panggilan() selalu mencetak “panggilan ke-1” karena variabel counter_panggilan diciptakan ulang sebagai nol setiap kali fungsi dimasuki. Sebaliknya, hitung_panggilan_static() mencetak angka yang terus bertambah—counter_panggilan hanya diinisialisasi sekali (saat program dimulai) dan nilainya bertahan di segmen data/bss.
Mari kita telaah baris-baris kode di atas:
- Baris 4 (
int counter_panggilan = 0;): deklarasi automatic. Tanda= 0dieksekusi ulang setiap kali fungsi dipanggil. Memori variabel ini berada di stack frame fungsi. - Baris 5 (
counter_panggilan++;): menambah nilai lokal menjadi1. Karena fungsi segera kembali, nilai ini dibuang bersama stack frame. - Baris 10 (
static int counter_panggilan = 0;): keywordstaticmengubah storage duration menjadi static. Inisialisasi= 0hanya berlaku sekali pada saat program dimulai (linker menempatkan variabel di segmen bss). - Baris 11 (
counter_panggilan++;): menambah nilai yang sama yang ada di segmen bss. Setiap pemanggilan fungsi melanjutkan nilai sebelumnya. - Baris 16 (
for (int i = 0; i < 3; i++)): loopiitu sendiri automatic storage di dalam blockfor—lifetime-nya hanya satu iterasi.
Allocated Storage: Lifetime Dikendalikan Programmer #
Berbeda dengan dua kategori sebelumnya, allocated storage memberi kita kendali penuh. Kita menentukan kapan memori diciptakan dan kapan dibebaskan. Pola yang aman adalah selalu memeriksa NULL, mengakses, lalu membebaskan dan menyetel pointer ke NULL.
#include <stdio.h>
#include <stdlib.h>
int *buat_int(int nilai) {
int *p = malloc(sizeof(int));
if (p) *p = nilai;
return p;
}
int main(void) {
int *q = buat_int(99);
if (q) {
printf("Nilai di heap: %d\n", *q);
free(q);
q = NULL; /* set ke NULL supaya tidak dangling */
if (q) {
printf("Masih punya pointer\n");
} else {
printf("q sudah NULL, akses aman dicek\n");
}
}
return 0;
}Output yang Dihasilkan:
Nilai di heap: 99
q sudah NULL, akses aman dicekSetelah free(q) dipanggil, memori yang ditunjuk q sudah dikembalikan ke allocator—namun pointer q sendiri masih menyimpan alamat lama (inilah dangling pointer). Baris q = NULL membersihkan alamat lama tersebut sehingga pengecekan if (q) selanjutnya akan menghasilkan false, mencegah akses tidak sengaja ke memori yang sudah dibebaskan.