Setiap array yang sudah kamu tulis sejauh ini ukurannya ditetapkan saat kompilasi: int scores[10]; menyediakan sepuluh slot, entah kamu butuh tiga atau tiga ratus. Itu berhasil sampai ukurannya baru diketahui saat program berjalan, misalnya berapa baris isi sebuah file atau berapa data yang dimasukkan pengguna. Memori dinamis memecahkan masalah itu: kamu meminta byte yang kamu butuhkan, saat kamu membutuhkannya, lalu mengembalikannya setelah selesai.
Alat-alatnya ada di <stdlib.h>:
#include <stdlib.h>
Kenapa Heap Ada
C memberi programmu dua area memori utama. Variabel lokal tinggal di automatic storage (stack): compiler menentukan ukurannya, dan variabel itu hilang begitu fungsinya selesai. Alokasi dinamis tinggal di heap: kamu menentukan ukurannya saat program berjalan, dan bloknya tetap hidup sampai kamu mem-free-nya secara eksplisit.
Sifat kedua itulah alasan sebenarnya memakai malloc. Sebuah fungsi bisa mengalokasikan blok, mengembalikan pointer-nya, dan memori itu masih valid di pemanggil, sesuatu yang tidak akan pernah bisa dilakukan array lokal. Kedua area ini dibandingkan secara detail di stack vs heap.
malloc: Meminta Byte
malloc menerima jumlah byte dan mengembalikan void * yang menunjuk ke memori sebesar itu yang belum diinisialisasi, atau NULL kalau permintaannya gagal.
Empat hal di program pendek itu adalah seluruh disiplinnya:
- Ekspresi ukurannya adalah
n * sizeof *scores. Bacalah sebagai "n buah dari apa pun yang ditunjukscores". - Hasilnya diperiksa terhadap
NULLsebelum dipakai. - Bloknya dipakai persis seperti array:
scores[i]berfungsi karena akses indeks adalah aritmetika pointer. - Blok di-free sekali, lalu pointer-nya diisi
NULL.
Perhatikan bahwa nilai kembaliannya tidak di-cast. Di C, void * otomatis dikonversi ke pointer objek apa pun, dan menulis (int *)malloc(...) hanya menambah keramaian sekaligus menyembunyikan include <stdlib.h> yang terlupa. (C++ mewajibkan cast itu; C tidak.)
Pola sizeof
Kenapa sizeof *scores, bukan sizeof(int)? Karena bentuk itu tidak bisa jadi usang.
int *a = malloc(n * sizeof *a); /* n ints, whatever int is here */
long *b = malloc(n * sizeof *b); /* same line shape, right size */
/* the fragile form */
long *c = malloc(n * sizeof(int)); /* compiles, allocates too little */
Baris terakhir adalah bug sungguhan yang tidak diperingatkan compiler mana pun: c adalah long *, alokasinya berukuran untuk int, dan setiap penulisan melewati separuh pertama akan kebablasan melewati ujung blok. Dengan sizeof *c, ukurannya otomatis mengikuti deklarasi.
sizeof *p tidak melakukan dereference pada p: sizeof dievaluasi saat kompilasi hanya dari tipenya, jadi aman bahkan kalau p belum diinisialisasi atau bernilai NULL.
Memori yang Belum Diinisialisasi
malloc tidak membersihkan memori yang diberikannya. Byte-byte itu berisi apa pun yang ada sebelumnya.
Kalau kamu ingin bloknya langsung bernilai nol, calloc melakukannya dalam satu langkah; lihat calloc dan realloc.
free: Mengembalikan Memori
free(p) mengembalikan blok ke allocator. Ada tiga aturan:
- Berikan pointer yang persis sama dengan yang dikembalikan
malloc, bukan pointer yang sudah digeser.free(p + 1)adalah undefined behavior. - Free setiap blok tepat sekali. Dua kali adalah double free, yang merusak catatan internal allocator itu sendiri.
- Setelah di-free, pointer-nya sudah usang. Memakainya adalah use-after-free.
int *p = malloc(sizeof *p);
free(p);
*p = 5; /* use-after-free - undefined behavior */
free(p); /* double free - undefined behavior */
Kedua baris itu tidak wajib langsung crash, dan justru itulah yang membuatnya berbahaya: program bisa berjalan beberapa menit lalu gagal di tempat yang tidak ada hubungannya. Pertahanan murahnya adalah mengosongkan pointer:
free(p);
p = NULL;
free(NULL) secara eksplisit didefinisikan tidak melakukan apa-apa, jadi free(p) kedua setelah itu tidak berbahaya, dan *p menjadi crash langsung di lokasi kesalahan sebenarnya, bukan kerusakan diam-diam.
Array Dinamis, dari Awal sampai Akhir
Berikut bentuk alokasi yang sebenarnya: baca jumlah saat program berjalan, tentukan ukuran blok darinya, pakai, lalu free.
Cast ke size_t pada jumlah itu penting begitu ukurannya membesar: n * sizeof *values dengan n bertipe int biasa bisa overflow sebelum hasil perkaliannya sampai ke malloc, sehingga menghasilkan blok yang jauh lebih kecil dari yang dimaksud. Mengalikan dalam size_t menghindarinya.
Mengalokasikan di Dalam Fungsi
Aturan umur heap itulah yang membuat ini sah, dan yang membuat mengembalikan array lokal tidak sah:
len + 1 menyisakan ruang untuk null terminator, aturan ukuran yang sama seperti di semua tempat lain pada string. Perhatikan juga komentar di atas fungsinya: saat sebuah fungsi mengembalikan memori yang dialokasikan, siapa yang harus mem-free-nya adalah bagian dari kontraknya. Menuliskannya bukan sekadar administrasi; itulah satu-satunya hal yang mencegah kebocoran.
Struct di Heap
Pola yang sama berlaku untuk tipe apa pun:
Perhatikan bahwa cabang kegagalan kedua mem-free p sebelum return. Begitu sebuah fungsi memegang lebih dari satu alokasi, setiap jalur error harus melepaskan alokasi yang sudah diambil. Inilah sumber paling umum dari kebocoran yang dijelaskan di kebocoran memori.
Daftar Periksa
- Selalu periksa
mallocterhadapNULLsebelum pemakaian pertama. - Tentukan ukuran dengan
n * sizeof *p, dengan perkalian dalamsize_t. - Jangan cast nilai kembaliannya di C.
- Memori dari
mallocbelum diinisialisasi; tulis dulu sebelum membaca. - Free tepat sekali, dengan pointer aslinya, lalu isi dengan
NULL. - Setiap jalur error setelah alokasi harus mem-free apa yang sudah dipegangnya.
Pertanyaan yang sering diajukan
Apa fungsi malloc di C?
malloc(n) meminta memori sebesar n byte dari heap dan mengembalikan pointer ke awal blok itu, atau NULL kalau permintaannya tidak bisa dipenuhi. Blok itu tetap ada sampai kamu memberikan pointer yang sama ke free. Berbeda dengan variabel lokal, blok ini tidak dilepaskan saat fungsi selesai.
Bagaimana cara memakai malloc dan free di C?
Alokasikan dengan int *p = malloc(n * sizeof *p);, periksa if (p == NULL) sebelum menyentuhnya, pakai seperti array, lalu free(p); tepat sekali setelah selesai. Mengisi p = NULL setelahnya membuat pemakaian tidak sengaja di kemudian hari menjadi crash yang jelas, bukan kerusakan data diam-diam.
Kenapa malloc(n * sizeof *p) lebih baik daripada malloc(n * sizeof(int))?
Karena sizeof *p otomatis mengikuti tipe pointer-nya. Kalau p nantinya menjadi long * atau struct Point *, ukuran alokasinya ikut menyesuaikan; menulis sizeof(int) secara langsung meninggalkan ukuran yang kini salah dan tidak akan ditandai oleh compiler.
Apa yang terjadi kalau memori tidak di-free di C?
Blok itu tetap teralokasi selama proses berjalan, yang disebut kebocoran memori (memory leak). Program pendek masih aman karena sistem operasi mengambil kembali semuanya saat program keluar, tetapi program yang berjalan lama bocor sedikit di setiap putaran dan akhirnya kehabisan memori. Lihat kebocoran memori.