Bir gösterici her zaman bir değer tutar. Gösterecek bir nesnesi olmadığında - atanmadan önce, gösterdiği şey serbest bırakıldıktan sonra ya da bir fonksiyonun döndürecek bir şeyi olmadığında - açıkça "burada bir şey yok" demek için bir değere ihtiyacı vardır. O değer boş göstericidir ve NULL, yazdığınız isimdir.
Etrafındaki disiplin küçüktür ve sürekli kendini öder: hedefleri olmadığında göstericileri NULL yapın, dereferans etmeden önce NULL kontrolü yapın ve sonucunuz yoksa NULL döndürün.
NULL Aslında Nedir
NULL, <stddef.h> içinde tanımlanan ve ayrıca <stdio.h>, <stdlib.h>, <string.h> ve diğerleri tarafından getirilen bir makrodur. Bir boş gösterici sabitine genişler - tipik olarak ((void*)0) ya da düz 0.
C standardı önemli olan özelliği garanti eder: bir boş gösterici, gerçek herhangi bir nesneye ya da fonksiyona giden bir göstericiye eşitsiz karşılaştırılır. Aynı tipteki iki boş gösterici her zaman birbirine eşit karşılaştırılır.
Çoğu sistem boş gösterici için (nil) ya da 0x0 yazdırır - sıfır adresi. Bu yaygın bir gerçekleştirimdir, bir kural değil; standart bitlerin sıfır olduğunu asla vaat etmez. Vaat ettiği şey karşılaştırmadır, dolayısıyla p == NULL yazın, gösterime bağlı hiçbir şey yazmayın.
NULL'u Dereferans Etmek Çökertir
Var olan her null denetiminin sebebi:
int *p = NULL;
printf("%d\n", *p); // tanımsız davranış - neredeyse kesinlikle bir çökme
Windows, macOS ve Linux'ta adres uzayının ilk sayfası bilerek eşlenmemiş bırakılır, dolayısıyla CPU erişimi yakalar ve işletim sistemi programı öldürür. Şunu göreceksiniz:
Segmentation fault (core dumped) # Linux
zsh: segmentation fault ./program # macOS
O anlık çökme bir özelliktir. Bir mantık hatasını, üç hafta sonra bulacağınız sessiz bir bozulma yerine ona yol açan tam satırda yüksek sesli bir başarısızlığa dönüştürür. Çökmeyi nasıl okuyup satırı nasıl bulacağınız için bkz. segmentation fault.
"Boş gösterici"nin C'de yakalayabileceğiniz bir istisna olmamasının sebebi budur. Kurtulma mekanizması yoktur; yalnızca önceden kontrol etme vardır.
Başarısız Olabilecekleri Kontrol Edin
Başarısız olabilen fonksiyonlar bunu NULL döndürerek bildirir. Kontrol etmek isteğe bağlı bir nezaket değildir - alternatifi çökmedir.
Standart kütüphane bunlarla doludur: malloc, calloc, realloc, fopen, strchr, strstr, getenv, bsearch. Her biri "sonuç yok"u NULL ile yanıtlar. Tahsisin tüm hikâyesi için bkz. dinamik bellek.
Denetim için ikisi de yaygın iki deyim:
if (p == NULL) { /* ele al */ } // açık - anlaşılırlık için tercih edilir
if (!p) { /* ele al */ } // kısa - NULL'un yanlış olmasına dayanır
Boş bir gösterici bir koşulda yanlış, geçerli bir gösterici doğru test edilir, dolayısıyla ikisi de çalışır. Açık biçim, bir yabancı kodunuzu hata için tararken daha iyi okunur.
NULL, 0 ve İlk Değersiz
Yeni başlayanların birbirine karıştırdığı üç ayrı durum:
int *a = NULL; "hicbir seyi gostermiyor" - test edilebilir, kontrolu guvenli
int *b; ilk degersiz - cop tutar, test EDILEMEZ
int *c = &x; gercek bir nesneyi gosteriyor
Tehlikeli olan b'dir. Baytları o yığın yuvasında ne kaldıysa odur; sıfır olabilir (ve iyi görünebilir) ya da daha önceki bir çağrıdan kalma akla yatkın görünen bir adres olabilir. Hiçbir denetim onu geçerli bir göstericiden ayırt edemez ve davranış hata ayıklama ile yayın derlemeleri arasında değişir.
Her göstericiye ilk değer verin. Henüz bir adresiniz yoksa NULL o adrestir. -Wall -Wextra ile derleyin, derleyici sizin için pek çok ilk değersiz kullanımı işaretleyecektir.
0 hakkında: bir gösterici bağlamında tam sayı sabiti 0, boş gösterici sabitidir, dolayısıyla p = 0; geçerli C'dir. Yine de NULL'u tercih edin. Niyeti işaret eder ve derleyicinin sizin için dönüşüm yapamadığı değişken argümanlı çağrılarda önemlidir:
execl("/bin/ls", "ls", 0); // riskli - gösterici beklenen yere int geçebilir
execl("/bin/ls", "ls", (char *)NULL); // doğru
C23, bu belirsizlik sınıfından tamamen kaçınan kendi türüne sahip bir anahtar kelime olan nullptr'ı ekliyor; NULL her yerde doğru ve taşınabilir kalıyor.
Savunmacı Kalıplar
Gösterici alan bir fonksiyonun tepesinde koruma koyun.
Serbest bıraktıktan sonra NULL yapın. free göstericinizi değiştirmez - yalnızca belleği serbest bırakır. Geride kalan bayat gösterici sarkan bir göstericidir ve onu kullanmak, çoğu zaman hemen çökmeyen tanımsız davranıştır.
O örnekteki iki olgu akılda tutmaya değer. free(NULL)'ın hiçbir şey yapmadığı tanımlıdır, dolayısıyla temizlik kodunun onu korumasına hiç gerek yoktur. Ve free'den sonra p = NULL yapmak, veriyi sessizce bozabilecek bir serbest-bırakma-sonrası-kullanımı anlık ve hata ayıklanabilir bir çökmeye dönüştürür.
"Sonuç yok" demek için NULL döndürün ve bunu söyleyin.
// eşleşen elemana bir gösterici döndürür, yoksa NULL
int *find(int *arr, int n, int target);
Bunu fonksiyonun üstündeki yorumda belgeleyin. Bir NULL'un mümkün olduğunu bilen bir çağıran denetimi yazar; bilmeyen yazmaz.
Hızlı Bir Kontrol Listesi
- Her göstericiye gerçek bir adres ya da
NULLile ilk değer verin. malloc,calloc,reallocvefopen'ın dönüşünü kullanmadan önce kontrol edin.- Kötü girdiyle çağrılabilecek her fonksiyonun tepesinde gösterici parametrelerini kontrol edin.
free'den hemen sonra göstericileriNULLyapın.- Bir gösterici kastedildiğinde
0değilNULLyazın. -Wall -Wextraile derleyin ve yapabildiğinizde bir denetleyici altında çalıştırın:gcc -fsanitize=address,undefinednull dereferansları kesin bir raporla yakalar.
Sıkça Sorulan Sorular
C'de NULL gösterici nedir?
Boş gösterici sabitini tutan bir göstericidir - gerçek herhangi bir nesnenin adresine eşitsiz karşılaştırılması garanti edilen bir değer. "Bu gösterici henüz hiçbir şeyi göstermiyor" demenin standart yoludur ve NULL, <stddef.h> ve birkaç başka başlıkta tanımlı makrosudur.
C'de bir NULL göstericiyi dereferans ederseniz ne olur?
Bu tanımsız davranıştır. Pratikte masaüstü ve sunucu sistemlerinde hemen bir segmentation fault ile çöker, çünkü sıfır adresi bilerek eşlenmemiş bırakılır. Bellek koruması olmayan gömülü sistemlerde bunun yerine sessizce bir şeyi okuyabilir ya da bozabilir ki bu çok daha kötüdür.
C'de NULL ile 0 aynı şey midir?
Gösterici bağlamlarında evet: tam sayı sabiti 0, boş gösterici sabitidir, dolayısıyla p = 0; ve p == NULL ikisi de çalışır. Ama göstericiler için NULL, sayılar için 0 kullanın - bu, okuyucuya hangisini kastettiğinizi söyler ve çıplak bir 0'ın gösterici yerine int olarak geçirilebileceği değişken argümanlı çağrıları güvende tutar.
NULL gösterici ile ilk değersiz gösterici arasındaki fark nedir?
Bir NULL gösterici kesinlikle hiçbir şeyi göstermez ve bunu test edebilirsiniz. İlk değersiz bir gösterici, o bellekte ne bayt varsa onu tutar - muhtemelen geçerli görünen bir adres - dolayısıyla onu yakalayabilecek bir test yoktur. Daha iyi bir şeyiniz yoksa göstericilere her zaman NULL ilk değerini verin.