realloc adalah fungsi yang memperbesar atau memperkecil blok memori yang sudah dialokasikan oleh malloc/calloc. Tanpa realloc, satu-satunya cara mengubah ukuran array dinamis adalah mengalokasikan blok baru, menyalin data, dan membebaskan blok lama—tepat seperti yang realloc lakukan secara internal. Yang istimewa, realloc sering bisa memperbesar blok in-place (di tempat yang sama) sehingga tidak perlu menyalin data; tetapi bila tidak bisa, realloc mengalokasikan blok baru, menyalin data lama, dan membebaskan blok lama—semua dalam satu panggilan. Pada chapter ini kita akan mempelajari kontrak realloc, menggunakannya untuk menumbuhkan array, dan membuktikan bahwa pointer lama boleh berubah.
Kontrak Fungsi realloc #
Fungsi realloc dideklarasikan di <stdlib.h> (ISO/IEC 9899 §7.22.3.5):
void *realloc(void *ptr, size_t size);Kontraknya:
ptrmenunjuk blok yang sebelumnya dialokasikan olehmalloc,calloc, ataurealloc. BilaptradalahNULL,reallocberperilaku sepertimalloc(size).sizeadalah ukuran baru dalam byte. Bilasizeadalah0danptrbukanNULL, perilaku implementation-defined; glibc membebaskan blok dan mengembalikanNULL.- Mengembalikan pointer baru — bisa sama dengan
ptr(perbesaran in-place) atau berbeda (blok dipindahkan). Data lama hinggamin(ukuran_lama, ukuran_baru)dijamin tetap utuh. - Bila
reallocgagal — mengembalikanNULL, dan blok lama tetap valid serta tidak dibebaskan. Programmer tetap harus memanggilfree(ptr)nantinya. - Isi byte baru di luar ukuran lama tidak terdefinisi —
realloctidak men-zero-kan wilayah baru (berbeda dengancalloc).
Menumbuhkan Array dengan realloc #
Pola klasik adalah memiliki array dengan kapasitas yang bisa tumbuh. Kita alokasikan kapasitas awal, lalu panggil realloc ketika data baru tidak muat.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
size_t kapasitas = 3;
int *arr = malloc(kapasitas * sizeof(int));
if (arr == NULL) {
fprintf(stderr, "malloc gagal\n");
return 1;
}
/* Isi 3 elemen awal */
for (size_t i = 0; i < kapasitas; i++) {
arr[i] = (int)(i * 10);
}
printf("Sebelum realloc: kapasitas=%zu, alamat=%p\n",
kapasitas, (void*)arr);
for (size_t i = 0; i < kapasitas; i++) {
printf(" arr[%zu] = %d\n", i, arr[i]);
}
/* Simpan alamat lama untuk dibandingkan */
int *alamat_lama = arr;
/* Tumbuhkan array dari 3 ke 6 elemen */
size_t kapasitas_baru = 6;
int *arr_baru = realloc(arr, kapasitas_baru * sizeof(int));
if (arr_baru == NULL) {
/* realloc gagal -- arr LAMA tetap valid, kita harus free manual */
free(arr);
fprintf(stderr, "realloc gagal\n");
return 1;
}
arr = arr_baru; /* arr sekarang menunjuk ke blok baru */
/* Isi 3 elemen baru */
for (size_t i = kapasitas; i < kapasitas_baru; i++) {
arr[i] = (int)(i * 10);
}
printf("\nSetelah realloc: kapasitas=%zu, alamat=%p\n",
kapasitas_baru, (void*)arr);
for (size_t i = 0; i < kapasitas_baru; i++) {
printf(" arr[%zu] = %d\n", i, arr[i]);
}
/* Apakah realloc mengembalikan blok baru? */
if (arr_baru != alamat_lama) {
printf("\n(Catat: realloc mengembalikan pointer baru)\n");
} else {
printf("\n(Catat: realloc memperbesar di tempat)\n");
}
free(arr);
return 0;
}Output yang Dihasilkan:
Sebelum realloc: kapasitas=3, alamat=0x559a001e42a0
arr[0] = 0
arr[1] = 10
arr[2] = 20
Setelah realloc: kapasitas=6, alamat=0x559a001e42a0
arr[0] = 0
arr[1] = 10
arr[2] = 20
arr[3] = 30
arr[4] = 40
arr[5] = 50
(Catat: realloc memperbesar di tempat)Pada kasus ini realloc berhasil memperbesar di tempat—alamat lama dan alamat baru sama. Data tiga elemen awal (0, 10, 20) tetap utuh tanpa perlu disalin manual.
Mari kita telaah baris-baris kode di atas:
- Baris 8 (
int *arr = malloc(kapasitas * sizeof(int));): alokasi awal array 3 elemen. Alamat awal dicatat kearr. - Baris 21 (
int *alamat_lama = arr;): menyimpan alamat blok lama untuk dibandingkan setelahrealloc. - Baris 25 (
int *arr_baru = realloc(arr, kapasitas_baru * sizeof(int));): meminta 6 elemen.reallocboleh mengembalikan pointer yang sama denganarr(perbesaran in-place) atau pointer baru (blok dipindahkan). Karena itu kita tidak boleh menulisarr = realloc(arr, ...)secara langsung—bila gagal, kita kehilangan satu-satunya pointer ke blok lama. - Baris 26 (
if (arr_baru == NULL)): penanganan kegagalan. Bilareallocgagal,arrlama tetap valid dan harus di-freemanual—tidak seperti kepercayaan populer bahwareallocmenghapusarr. - Baris 32 (
arr = arr_baru;): setelah realloc sukses, kita aman menimpaarrdengan pointer baru. Pointerarrlama tidak lagi valid (blok lama sudah dibebaskan olehreallocbila dipindahkan). - Baris 35-37 (
arr[i] = ...): mengisi 3 elemen baru. Wilayah baru ini tidak di-zero-kan olehrealloc; bila kita tidak mengisinya, isinya tak terdefinisi.
Saat realloc Memindahkan Blok #
realloc memperbesar in-place hanya bila ada ruang bebas tepat setelah blok lama. Bila ada alokasi lain yang menghalangi, realloc harus memesan blok baru, menyalin data lama, dan membebaskan blok lama. Kita buktikan dengan mengganggu heap.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main(void) {
/* Alokasi kecil diikuti alokasi besar di tengah blok agar realloc
harus memindahkan blok lama ke lokasi baru */
int *a = malloc(16 * sizeof(int));
if (!a) return 1;
for (int i = 0; i < 16; i++) a[i] = i + 1;
int *pengganggu = malloc(8 * sizeof(int));
if (!pengganggu) return 1;
for (int i = 0; i < 8; i++) pengganggu[i] = -1;
/* Simpan alamat sebagai nilai numerik untuk dibandingkan, karena
setelah realloc pointer lama mungkin tidak valid */
uintptr_t alamat_lama = (uintptr_t)a;
/* realloc dari 16 ke 1024 elemen - tidak ada ruang ekspansi, harus pindah */
int *b = realloc(a, 1024 * sizeof(int));
if (!b) { free(a); free(pengganggu); return 1; }
uintptr_t alamat_baru = (uintptr_t)b;
if (alamat_baru != alamat_lama) {
printf("realloc memindahkan blok: lama=0x%lx, baru=0x%lx\n",
(unsigned long)alamat_lama, (unsigned long)alamat_baru);
} else {
printf("realloc memperbesar di tempat: alamat=0x%lx\n",
(unsigned long)alamat_baru);
}
/* Cek 16 elemen lama tetap utuh */
printf("Data lama tetap utuh? ");
int utuh = 1;
for (int i = 0; i < 16; i++) {
if (b[i] != i + 1) { utuh = 0; break; }
}
printf("%s\n", utuh ? "ya" : "tidak");
free(b);
free(pengganggu);
return 0;
}Output yang Dihasilkan:
realloc memindahkan blok: lama=0x56307d58f2a0, baru=0x56307d58f320
Data lama tetap utuh? yaKarena alokasi pengganggu berada tepat setelah blok a di heap, realloc tidak bisa memperbesar a in-place. Alih-alih, realloc memesan blok baru berukuran 1024 elemen, menyalin 16 elemen lama, dan membebaskan blok a lama. Hasilnya: alamat baru berbeda (0x...320 bukan 0x...2a0), tetapi data 16 elemen pertama tetap utuh (1, 2, ..., 16).
Kita menggunakan uintptr_t dari <stdint.h> untuk menyimpan alamat sebagai nilai integer. Trik ini menghindari peringatan kompiler -Wuse-after-free: setelah realloc, pointer a lama bisa jadi sudah tidak valid, tetapi nilai integer alamat masih bisa dibandingkan tanpa dereference.