Wild pointer adalah pointer yang dideklarasikan tetapi tidak diinisialisasi. Karena tidak diberi nilai awal, pointer berisi “sampah” dari byte-byte yang kebetulan ada di region stack yang sama—biasanya alamat acak yang mungkin valid atau tidak. Dereference wild pointer adalah undefined behavior paling tidak terprediksi di C: kadang crash dengan segfault, kadang diam-diam menulis ke memori yang valid dan merusak data tanpa peringatan. Pada chapter ini kita akan mendemonstrasikan wild pointer, menjelaskan mengapa ini sangat berbahaya, dan menunjukkan tiga cara mencegahnya.

Apa Itu Wild Pointer #

Saat sebuah variabel pointer dideklarasikan tanpa inisialisasi (int *p;), compiler memesan 8 byte (pada sistem 64-bit) untuk variabel p di stack frame, tetapi tidak mengisi nilai awal apa pun. Isi byte-byte tersebut adalah apa pun yang kebetulan ada di memori stack sebelumnya—biasanya sisa dari fungsi lain yang sudah selesai. Alamat ini bisa berupa apa saja: alamat valid di stack, alamat di heap, atau alamat di region yang tidak terpetakan.

Mekanisme kerjanya: saat compiler melihat int *p; tanpa inisialisasi, ia hanya memesan ruang untuk variabel tetapi tidak mengatur nilainya. Saat program kemudian menulis *p = 5; (dereference wild pointer), program menulis nilai 5 ke alamat acak yang tersimpan di p. Jika alamat itu kebetulan valid (misalnya region stack), penulisan berhasil tanpa crash tetapi merusak data di alamat tersebut—bug yang sangat sulit dilacak. Jika alamat tidak terpetakan, program langsung crash dengan segfault.

c
#include <stdio.h>

int main(void) {
    /* Wild pointer: dideklarasikan tapi tidak diinisialisasi */
    int *p;   /* isi = sampah dari stack */

    printf("Nilai pointer liar p = %p\n", (void*)p);

    /* Mencoba dereference = UB! Bisa crash, bisa diam-diam rusak data */
    /* Baris berikut sengaja tidak di-comment untuk demonstrasi - HATI-HATI */
    /* *p = 5;   */

    /* Praktek benar: inisialisasi saat deklarasi */
    int x = 10;
    int *q = &x;
    printf("Pointer terinisialisasi q -> %p, *q = %d\n", (void*)q, *q);

    /* Atau set ke NULL jika belum ada alamat valid */
    int *r = NULL;
    printf("Pointer NULL r -> %p (aman untuk diperiksa)\n", (void*)r);

    return 0;
}

Output yang Dihasilkan:

text
Nilai pointer liar p = 0x7ffedac65f30
Pointer terinisialisasi q -> 0x7ffedac65e84, *q = 10
Pointer NULL r -> (nil) (aman untuk diperiksa)

Pointer liar p kebetulan berisi alamat 0x7ffedac65f30—region stack yang masih valid. Inilah bahaya wild pointer: alamat acak kadang valid, kadang tidak. Pada eksekusi ini, p menunjuk ke alamat di stack; pada eksekusi lain, p bisa menunjuk ke alamat yang tidak terpetakan dan dereference akan menimbulkan segfault. Compiler GCC dengan -Wall -Wextra memunculkan peringatan -Wmaybe-uninitialized saat kita mencoba membaca p. Peringatan ini wajib diperhatikan—jangan pernah abaikan.

Mari kita telaah baris-baris kode di atas:

  1. Baris 5 (int *p;): deklarasi pointer tanpa inisialisasi. Compiler memesan 8 byte tapi tidak mengisi—ini adalah wild pointer.
  2. Baris 7 (printf("... p = %p", (void*)p);): mencetak alamat liar. Kompile dengan -Wall -Wextra memunculkan -Wmaybe-uninitialized karena p belum diberi nilai.
  3. Baris 12-13 (int x = 10; int *q = &x;): praktek benar—inisialisasi langsung dengan alamat variabel yang valid.
  4. Baris 17 (int *r = NULL;): praktek alternatif—set ke NULL jika belum ada alamat valid. Cek if (r != NULL) sebelum dereference.
  5. Baris 8-10 (/* *p = 5; */): baris yang dikomentari—dereference wild pointer. Di-uncomment, ini UB yang bisa crash atau merusak data.

Demonstrasi Dereference Wild Pointer #

Inilah yang terjadi bila kita nekat dereference wild pointer. Demo berikut dikompilasi dengan -Wall -Wextra dan kemudian dijalankan:

c
#include <stdio.h>

int main(void) {
    int *p;          /* wild pointer - berisi sampah stack */
    /* Compiler GCC mengeluarkan -Wmaybe-uninitialized */
    *p = 5;          /* UB: menulis ke alamat acak */
    printf("*p = %d\n", *p);
    return 0;
}

Output yang Dihasilkan:

text
*p = 5

Compiler memperingatkan: 'p' is used uninitialized [-Wuninitialized]. Namun saat dijalankan, program tidak crash—malah mencetak *p = 5 dengan sukses. Inilah skenario terburuk UB: program tampak berjalan baik tetapi sebenarnya telah menulis ke alamat acak di stack, mungkin merusak variabel lain atau metadata. Pada eksekusi berbeda dengan kondisi stack yang berbeda, program yang sama bisa saja crash. Inilah mengapa UB begitu berbahaya—perilaku tidak terprediksi membuat bug muncul dan hilang secara acak, sangat sulit direproduksi.