Sejauh ini kita mendeklarasikan struct sebagai variabel lokal yang hidupnya terbatas pada blok fungsi. Untuk data yang ukurannya baru diketahui saat runtime, atau yang perlu bertahan di luar fungsi pembuatnya, kita perlu mengalokasikan struct di heap memakai malloc. Hasil dari malloc adalah pointer ke blok memori tersebut, sehingga kita memerlukan cara untuk mengakses member struct lewat pointer. Di sinilah operator panah -> berperan kembali. Pada chapter ini kita akan belajar mengalokasikan struct secara dinamis, mengisinya lewat pointer, dan melepaskannya dengan free untuk menghindari memory leak.
Mengapa Pointer to struct #
Pointer to struct memberikan dua manfaat utama. Pertama, kita bisa mengalokasikan struct di heap sehingga masa hidupnya tidak terikat pada blok fungsi—struct bisa diciptakan di dalam fungsi pembuat dan dipakai lagi di tempat lain setelah fungsi tersebut selesai. Kedua, pointer memungkinkan kita membangun struktur data yang lebih kompleks: linked list, tree, dan graph semuanya memakai pointer to struct untuk merujuk node lain.
Mekanisme alokasi: malloc(sizeof(struct T)) meminta sistem operasi sejumlah byte di heap, lalu mengembalikan pointer bertipe void *. Kita meng-cast-nya (secara implisit di C) ke struct T * melalui assignment. Memori yang dialokasikan tidak diinisialisasi—member-member berisi nilai sampah, jadi kita wajib mengisi semuanya sebelum dibaca. Setelah selesai, free(ptr) mengembalikan blok memori ke heap.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Mahasiswa {
char nama[50];
int umur;
float ipk;
};
int main(void) {
/* Alokasi struct di heap via malloc */
struct Mahasiswa *mhs = malloc(sizeof(struct Mahasiswa));
if (mhs == NULL) {
fprintf(stderr, "Gagal mengalokasikan memori\n");
return 1;
}
/* Isi member via pointer dan operator arrow */
strcpy(mhs->nama, "Budi Santoso");
mhs->umur = 21;
mhs->ipk = 3.75f;
/* Baca kembali */
printf("Nama : %s\n", mhs->nama);
printf("Umur : %d\n", mhs->umur);
printf("IPK : %.2f\n", mhs->ipk);
/* Lepas memori */
free(mhs);
mhs = NULL; /* Hindari dangling pointer */
return 0;
}Output yang Dihasilkan:
Nama : Budi Santoso
Umur : 21
IPK : 3.75Mari kita telaah baris-baris kode di atas:
- Baris 13 (
struct Mahasiswa *mhs = malloc(sizeof(struct Mahasiswa));): pemanggilanmallocmeminta blok memori seukuranstruct Mahasiswadi heap.sizeof(struct Mahasiswa)adalah jumlah byte yang dibutuhkan, termasuk padding. Pointermhskemudian menunjuk ke blok tersebut. - Baris 14-17 (
if (mhs == NULL)): pemeriksaan wajib.mallocbisa gagal (misalnya ketika memori habis) dan mengembalikanNULL. Mengakses member melalui pointerNULLadalah undefined behavior—biasanya segfault. - Baris 20 (
strcpy(mhs->nama, "Budi Santoso");): mengisi membernama. Karenamhsadalah pointer, kita pakai operator panah->. Untuk member array, kita tidak bisa menulismhs->nama = "Budi";(array tidak bisa di-assign), sehinggastrcpytetap diperlukan. - Baris 21-22 (
mhs->umur = 21; mhs->ipk = 3.75f;): assignment langsung untuk memberintdanfloat. Suffixfmembuat literal3.75bertipefloat. - Baris 27 (
free(mhs);): mengembalikan blok memori ke heap. Setelahfree, memori tersebut bisa dialokasikan ulang untuk keperluan lain. Membaca atau menulis melaluimhssetelahfreeadalah use-after-free—bug berbahaya yang bisa menyebabkan crash atau korupsi memori. - Baris 28 (
mhs = NULL;): menetapkan pointer keNULLsetelahfreeadalah praktik defensif. Jika kode berikutnya tidak sengaja mengaksesmhs, program akan crash segera (segfault saat aksesNULL) alih-alih merusak data secara diam-diam.
Array of Pointer to struct #
Kombinasi malloc dan array of pointer memungkinkan kita membuat koleksi struct yang ukurannya baru diketahui saat runtime—sesuatu yang tidak mungkin dengan array of struct statis.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char nama[40];
int umur;
float ipk;
} Mahasiswa;
/* Fungsi yang mengalokasikan struct di heap */
Mahasiswa *buat_mahasiswa(const char *nama, int umur, float ipk) {
Mahasiswa *m = malloc(sizeof(Mahasiswa));
if (m == NULL) return NULL;
strncpy(m->nama, nama, sizeof(m->nama) - 1);
m->nama[sizeof(m->nama) - 1] = '\0'; /* pastikan null-terminated */
m->umur = umur;
m->ipk = ipk;
return m;
}
/* Fungsi yang menerima pointer untuk modifikasi */
void naik_semester(Mahasiswa *m) {
m->umur += 1;
}
int main(void) {
/* Array of pointer to struct */
Mahasiswa *daftar[3];
daftar[0] = buat_mahasiswa("Budi Santoso", 20, 3.65f);
daftar[1] = buat_mahasiswa("Siti Aminah", 21, 3.85f);
daftar[2] = buat_mahasiswa("Andi Wijaya", 19, 3.55f);
if (daftar[0] == NULL || daftar[1] == NULL || daftar[2] == NULL) {
fprintf(stderr, "Alokasi gagal\n");
return 1;
}
/* Naikkan semester semua mahasiswa */
for (int i = 0; i < 3; i++) {
naik_semester(daftar[i]);
}
/* Cetak hasil */
for (int i = 0; i < 3; i++) {
printf("%d. %-15s umur=%d IPK=%.2f\n",
i + 1, daftar[i]->nama, daftar[i]->umur, daftar[i]->ipk);
}
/* Lepas semua memori */
for (int i = 0; i < 3; i++) {
free(daftar[i]);
daftar[i] = NULL;
}
return 0;
}Output yang Dihasilkan:
1. Budi Santoso umur=21 IPK=3.65
2. Siti Aminah umur=22 IPK=3.85
3. Andi Wijaya umur=20 IPK=3.55Pada contoh di atas, Mahasiswa *daftar[3] adalah array berisi 3 pointer ke Mahasiswa. Setiap elemen diisi dengan hasil buat_mahasiswa(...), yang mengalokasikan Mahasiswa baru di heap dan mengembalikan pointer. Karena setiap elemen array adalah pointer, akses member memakai dua tingkat: daftar[i] untuk mengambil pointer, lalu ->nama untuk akses member. Loop naik_semester(daftar[i]) memodifikasi tiap struct lewat pointer. Setelah selesai, loop terakhir memanggil free untuk setiap pointer—kalau ada satu yang terlewat, memori tersebut bocor selamanya.