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.

c
#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:

text
Nama : Budi Santoso
Umur : 21
IPK  : 3.75

Mari kita telaah baris-baris kode di atas:

  1. Baris 13 (struct Mahasiswa *mhs = malloc(sizeof(struct Mahasiswa));): pemanggilan malloc meminta blok memori seukuran struct Mahasiswa di heap. sizeof(struct Mahasiswa) adalah jumlah byte yang dibutuhkan, termasuk padding. Pointer mhs kemudian menunjuk ke blok tersebut.
  2. Baris 14-17 (if (mhs == NULL)): pemeriksaan wajib. malloc bisa gagal (misalnya ketika memori habis) dan mengembalikan NULL. Mengakses member melalui pointer NULL adalah undefined behavior—biasanya segfault.
  3. Baris 20 (strcpy(mhs->nama, "Budi Santoso");): mengisi member nama. Karena mhs adalah pointer, kita pakai operator panah ->. Untuk member array, kita tidak bisa menulis mhs->nama = "Budi"; (array tidak bisa di-assign), sehingga strcpy tetap diperlukan.
  4. Baris 21-22 (mhs->umur = 21; mhs->ipk = 3.75f;): assignment langsung untuk member int dan float. Suffix f membuat literal 3.75 bertipe float.
  5. Baris 27 (free(mhs);): mengembalikan blok memori ke heap. Setelah free, memori tersebut bisa dialokasikan ulang untuk keperluan lain. Membaca atau menulis melalui mhs setelah free adalah use-after-free—bug berbahaya yang bisa menyebabkan crash atau korupsi memori.
  6. Baris 28 (mhs = NULL;): menetapkan pointer ke NULL setelah free adalah praktik defensif. Jika kode berikutnya tidak sengaja mengakses mhs, program akan crash segera (segfault saat akses NULL) 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.

c
#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:

text
1. Budi Santoso    umur=21 IPK=3.65
2. Siti Aminah     umur=22 IPK=3.85
3. Andi Wijaya     umur=20 IPK=3.55

Pada 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.