Menu

Pointer NULL di C: arti NULL dan cara mengeceknya

NULL adalah alamat yang disimpan pointer saat tidak menunjuk ke apa pun. Halaman ini membahas apa sebenarnya NULL, kenapa dereference NULL membuat program crash, bedanya dengan 0 dan dengan pointer yang belum diinisialisasi, dan pola bertahan yang menjauhkan bug null dari kodemu.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Pointer selalu menyimpan suatu nilai. Saat belum ada objek untuk ditunjuk, misalnya sebelum diberi nilai, setelah objek yang ditunjuknya dibebaskan, atau saat sebuah fungsi tidak punya apa-apa untuk dikembalikan, pointer butuh nilai yang jelas berarti "tidak ada apa-apa di sini". Nilai itu adalah null pointer, dan NULL adalah nama yang kamu tulis.

Disiplin seputar NULL itu kecil dan terus-menerus memberi manfaat: set pointer ke NULL saat tidak punya target, cek NULL sebelum dereference, dan kembalikan NULL saat kamu tidak punya hasil.

Apa sebenarnya NULL

NULL adalah makro, didefinisikan di <stddef.h> dan juga ikut terbawa oleh <stdio.h>, <stdlib.h>, <string.h>, dan header lainnya. Makro ini diekspansi menjadi konstanta null pointer, biasanya ((void*)0) atau 0 saja.

Standar C menjamin sifat yang penting: null pointer tidak sama dengan pointer ke objek atau fungsi sungguhan mana pun. Dua null pointer dengan tipe yang sama selalu sama satu sama lain.

Kebanyakan sistem mencetak (nil) atau 0x0 untuk null pointer, yaitu alamat nol. Itu implementasi yang umum, bukan aturan; standarnya tidak pernah menjanjikan bahwa bit-bitnya nol. Yang dijanjikan adalah perbandingannya, jadi tulis p == NULL, jangan pernah sesuatu yang bergantung pada representasinya.

Dereference NULL membuat crash

Inilah alasan di balik setiap pengecekan null yang pernah ada:

int *p = NULL;
printf("%d\n", *p);   // undefined behavior - almost certainly a crash

Di Windows, macOS, dan Linux, halaman pertama ruang alamat sengaja dibiarkan tidak dipetakan, jadi CPU menangkap akses itu dan sistem operasi menghentikan programnya. Kamu akan melihat:

Segmentation fault (core dumped)          # Linux
zsh: segmentation fault  ./program        # macOS

Crash yang langsung terjadi itu adalah fitur. Crash itu mengubah bug logika menjadi kegagalan yang jelas di baris yang menyebabkannya, bukan kerusakan diam-diam yang baru kamu temukan tiga minggu kemudian. Lihat halaman segmentation fault untuk cara membaca crash dan menemukan barisnya.

Karena itulah "null pointer" bukan exception yang bisa kamu tangkap di C. Tidak ada mekanisme untuk memulihkan program; yang ada hanya pengecekan sebelumnya.

Cek apa yang mungkin gagal

Fungsi yang bisa gagal memberi tanda dengan mengembalikan NULL. Mengeceknya bukan sekadar sopan santun yang opsional: alternatifnya adalah crash.

Library standar penuh dengan fungsi seperti ini: malloc, calloc, realloc, fopen, strchr, strstr, getenv, bsearch. Semuanya menjawab "tidak ada hasil" dengan NULL. Lihat halaman memori dinamis untuk cerita lengkap soal alokasi.

Ada dua idiom untuk pengecekannya, dan keduanya umum:

if (p == NULL) { /* handle */ }   // explicit - preferred for clarity
if (!p)        { /* handle */ }   // terse - relies on NULL being falsy

Null pointer dianggap salah di dalam kondisi dan pointer yang valid dianggap benar, jadi keduanya bekerja. Bentuk yang eksplisit lebih mudah dibaca saat orang lain menelusuri kodemu untuk mencari bug.

NULL vs 0 vs belum diinisialisasi

Tiga situasi berbeda yang sering dicampuradukkan pemula:

int *a = NULL;     "points at nothing" - testable, safe to check
int *b;            uninitialized - holds garbage, NOT testable
int *c = &x;       points at a real object

Yang berbahaya adalah b. Byte-nya adalah apa pun yang tersisa di slot stack itu, bisa nol (dan kelihatan baik-baik saja) atau bisa juga alamat yang kelihatan masuk akal dari pemanggilan sebelumnya. Tidak ada pengecekan yang bisa membedakannya dari pointer yang valid, dan perilakunya berubah antara build debug dan build rilis.

Inisialisasi setiap pointer. Kalau belum punya alamat, NULL adalah alamatnya. Kompilasi dengan -Wall -Wextra dan compiler akan menandai banyak pemakaian yang belum diinisialisasi untukmu.

Soal 0: dalam konteks pointer, konstanta integer 0 adalah konstanta null pointer, jadi p = 0; adalah C yang valid. Tetap utamakan NULL. Itu menunjukkan maksudmu, dan penting di pemanggilan variadic, di mana compiler tidak bisa melakukan konversi untukmu:

execl("/bin/ls", "ls", 0);       // risky - may pass an int where a pointer is expected
execl("/bin/ls", "ls", (char *)NULL);   // correct

C23 menambahkan nullptr, kata kunci dengan tipenya sendiri yang menghindari ambiguitas semacam ini sepenuhnya; NULL tetap benar dan portabel di mana saja.

Pola bertahan

Jaga di bagian atas fungsi yang menerima pointer.

Set ke NULL setelah membebaskan memori. free tidak mengubah pointermu: fungsi itu hanya melepaskan memorinya. Pointer basi yang tertinggal disebut pointer dangling, dan memakainya adalah undefined behavior yang sering tidak langsung membuat crash.

Ada dua fakta di dalam contoh itu yang layak diingat. free(NULL) didefinisikan tidak melakukan apa-apa, jadi kode pembersihan tidak perlu menjaganya. Lalu menulis p = NULL setelah free mengubah use-after-free, yang bisa diam-diam merusak data, menjadi crash langsung yang bisa di-debug.

Kembalikan NULL untuk berarti "tidak ada hasil", dan katakan itu.

// returns a pointer to the matching element, or NULL if there is none
int *find(int *arr, int n, int target);

Tulis di komentar di atas fungsinya. Pemanggil yang tahu bahwa NULL mungkin muncul akan menulis pengecekannya; pemanggil yang tidak tahu, tidak akan.

Daftar cek singkat

  • Inisialisasi setiap pointer, dengan alamat sungguhan atau dengan NULL.
  • Cek nilai kembalian malloc, calloc, realloc, dan fopen sebelum memakainya.
  • Cek parameter pointer di bagian atas setiap fungsi yang bisa dipanggil dengan input yang buruk.
  • Set pointer ke NULL segera setelah free.
  • Tulis NULL, bukan 0, setiap kali yang dimaksud adalah pointer.
  • Build dengan -Wall -Wextra dan, kalau bisa, jalankan dengan sanitizer: gcc -fsanitize=address,undefined menangkap dereference null dengan laporan yang tepat.

Pertanyaan yang sering diajukan

Apa itu pointer NULL di C?

Pointer NULL adalah pointer yang menyimpan konstanta null pointer, yaitu nilai yang dijamin tidak sama dengan alamat objek sungguhan mana pun. Ini cara standar untuk mengatakan "pointer ini belum menunjuk ke apa pun", dan NULL adalah makronya, yang didefinisikan di <stddef.h> dan beberapa header lain.

Apa yang terjadi kalau pointer NULL di-dereference di C?

Hasilnya undefined behavior. Dalam praktiknya, di sistem desktop dan server, program langsung crash dengan segmentation fault karena alamat nol sengaja dibiarkan tidak dipetakan. Di sistem embedded tanpa proteksi memori, program mungkin malah diam-diam membaca atau merusak sesuatu, dan itu jauh lebih buruk.

Apakah NULL sama dengan 0 di C?

Dalam konteks pointer, ya: konstanta integer 0 adalah konstanta null pointer, jadi p = 0; dan p == NULL sama-sama bekerja. Tapi pakai NULL untuk pointer dan 0 untuk angka: itu memberi tahu pembaca mana yang kamu maksud, dan menjaga pemanggilan variadic tetap aman, karena di sana 0 saja bisa terkirim sebagai int, bukan sebagai pointer.

Apa bedanya pointer NULL dan pointer yang belum diinisialisasi?

Pointer NULL pasti tidak menunjuk ke apa pun, dan kamu bisa mengujinya. Pointer yang belum diinisialisasi menyimpan byte apa saja yang kebetulan ada di memori itu, mungkin berupa alamat yang kelihatan valid, sehingga tidak ada uji yang bisa menangkapnya. Selalu inisialisasi pointer dengan NULL kalau kamu belum punya nilai yang lebih baik.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai