C / C ++ 'da NULL işaretçisi kontrol ediliyor [kapalı]


153

Son zamanlarda yapılan bir kod incelemesinde, bir katılımcı NULLişaretçiler üzerindeki tüm kontrollerin aşağıdaki şekilde yapılmasını zorunlu kılmaya çalışmaktadır :

int * some_ptr;
// ...
if (some_ptr == NULL)
{
    // Handle null-pointer error
}
else
{
    // Proceed
}

onun yerine

int * some_ptr;
// ...
if (some_ptr)
{
    // Proceed
}
else
{
    // Handle null-pointer error
}

Açıkça "Bu işaretçinin NULL olmadığından emin olun" derken yolunun biraz daha açık olduğunu kabul ediyorum, ancak bu kod üzerinde çalışan herkesin bir işaretçi değişkenini kullanarak bir ififadesi dolaylı olarak kontrol ediyor NULL. Ayrıca ikinci yöntemin ilk bir hata tanıtmak için daha küçük bir şans olduğunu hissediyorum:

if (some_ptr = NULL)

bu sadece bulmak ve hata ayıklamak için mutlak bir acıdır.

Hangi yolu tercih edersiniz ve neden?


32
Tutarlı bir stile sahip olmak bazen hangi stili seçtiğinizden daha önemlidir.
Mark Ransom

15
@ Mark: Tutarlılık her zaman tarzınızın en önemli faktörüdür.
sbi

13
"Ayrıca ikinci yöntemin ilk bir hata tanıtmak için daha küçük bir şans olduğunu hissediyorum: if (some_ptr = NULL)" Bu yüzden bazı insanlar (NULL == some_ptr), NULL atayamazsanız kullanın, bu yüzden olsun derleme hatası.
user122302

14
Çoğu derleyici bugünlerde şartlı olarak atama konusunda uyarıyor - maalesef çok fazla geliştirici derleyicinin uyarılarını görmezden geliyor.
Mike Ellery

10
Yukarıdaki ifade ile tutarlılığın önemli olduğunu kabul etmiyorum. Bunun tutarlılıkla ilgisi yoktur; İyi tür değil zaten. Bu, programcıların kendi stillerini ifade etmelerine izin vermemiz gereken alandır. Her iki formu da hemen anlamayan bir kişi varsa, kodu hemen okumayı bırakmalı ve bir kariyer değişikliği düşünmelidir. Bundan daha fazlasını söyleyeceğim: Bir kozmetik kıvamı varsa, bir komut dosyasıyla otomatik olarak uygulayamazsınız, ardından kaldırın. İnsan moral boşaltma maliyetidir YOLU insanlar bir toplantıda yapmak o aptal kararlar daha önemlidir.
wilhelmtell

Yanıtlar:


203

Deneyimlerime göre, form if (ptr)veya test testleri if (!ptr)tercih edilir. Sembolün tanımına bağlı değildirler NULL. Kazara görevlendirme fırsatını ortaya çıkarmazlar. Ve onlar açık ve özlü.

Düzenleme: SoapBox bir açıklamada işaret ettiği gibi, bunlar auto_ptrgibi işaretçiler olarak hareket eden ve booltam olarak bu deyimi etkinleştirmek için bir dönüşüm sağlayan nesneler gibi C ++ sınıfları ile uyumludur . Bu nesneler için, açık bir karşılaştırma NULL, diğer semantik yan etkilere sahip olabilen veya booldönüşümün ima ettiği basit varoluş kontrolünden daha pahalı olabilen işaretçiye bir dönüşüm çağırmak zorunda kalacaktır .

Gereksiz metin olmadan ne anlama geldiğini söyleyen kod için bir tercih var. if (ptr != NULL)aynı if (ptr)özgüllük pahasına aynı anlama sahiptir . Bir sonraki mantıklı şey yazmaktır if ((ptr != NULL) == TRUE)ve bu şekilde delilik yatar. C dili, bir Boole ile test açıktır if, whileveya sıfır değerinin anlamı, belirli bir yer alır gibi gerçek ve sıfır yanlıştır. Artıklık bunu netleştirmez.


23
Ayrıca, genellikle operatör boolünü (veya safe_bool) geçersiz kılan işaretçi sarma sınıflarıyla (paylaşılan_ptr, auto_ptr, scoped_tr, vb.) Uyumludur.
SoapBox

2
Ben sadece tartışmaya auto_ptr ekleme referans nedeniyle "cevap" olarak oy. Soruyu yazdıktan sonra, bunun aslında herhangi bir şeyden daha öznel olduğu ve muhtemelen koşullara bağlı olarak "doğru" olabileceğinden yığın akışı "yanıtı" için uygun olmadığı açıktır.
Bryan Marble

2
@Bryan, buna saygı duyuyorum ve "daha iyi" bir cevap gelirse, lütfen onay işaretini gerektiği gibi hareket ettirmekten çekinmeyin. Öznellik gelince, evet özneldir. Ancak, en azından soru ortaya çıktığında her zaman açık olmayan nesnel sorunların olduğu öznel bir tartışmadır. Çizgi bulanık. Ve öznel ... ;-)
RBerteig

1
Dikkat edilmesi gereken önemli bir nokta: yakın zamana kadar, daha özlü olduğu için "if (ptr! = NULL)" veya "if (ptr! = Nullptr)" yerine "if (ptr)" yazmaktan yanaydım. kod daha okunabilir. Ancak yeni karşılaştırmanın son derleyicilerdeki NULL / nullptr ile karşılaştırmak için uyarı / hata verdiğini öğrendim. Eğer bir işaretçiyi kontrol etmek istiyorsanız, "if (ptr)" yerine "if (ptr! = NULL)" yapmalısınız. Yanlışlıkla ptr'yi int olarak ilan ederseniz, ilk kontrol bir uyarı / hata verirken, ikincisi herhangi bir uyarı yapmadan derlenir.
Arnaud

1
@David 天宇 Wong Hayır, kastedilen bu değildi. Bu sayfadaki örnek int y = *x; if (!x) {...}, optimize edicinin NULL *xise tanımsız olacağından x, NULL olmamalı ve bu nedenle !xFALSE olması gerektiği gerekçesine izin verilen iki satır dizisidir . İfadesi !ptrolduğunu değil davranışı tanımsız. Örnekte, test herhangi bir kullanımdan önce yapılmışsa *x, optimize edici testi ortadan kaldırmak için yanlış olur.
RBerteig

52

if (foo)yeterince açıktır. Kullanın.


25

Bununla başlayacağım: tutarlılık kraldır, karar kod tabanınızdaki tutarlılıktan daha az önemlidir.

C ++ dilinde

NULL, C ++ 'da 0 veya 0L olarak tanımlanır.

C ++ Programlama Dili'ni okuduysanız Bjarne Stroustrup, atama yaparken makrodan 0kaçınmak için açıkça kullanmanızı önerirNULL , karşılaştırmalarıyla aynı şeyi yapıp yapmadığından emin değilim, kitabı okuduğumdan beri bir süre geçti, sanırım sadece yaptı if(some_ptr)açık bir karşılaştırma olmadan ama ben bu konuda bulanık.

Bunun nedeni, NULLmakronun aldatıcı olması (neredeyse tüm makrolar gibi) aslında 0gerçek anlamlıdır, adından da anlaşılacağı gibi benzersiz bir tür değildir. Makrolardan kaçınmak, C ++ 'daki genel yönergelerden biridir. Öte yandan, 0bir tamsayı gibi görünür ve işaretçilerle karşılaştırıldığında ya da atanmış değildir. Şahsen ben her iki şekilde de gidebilirim, ama tipik olarak açık karşılaştırmayı atlıyorum (bazı insanlar bunu sevmiyor olsa da, muhtemelen neden bir değişiklik öneren bir katkıda bulunuyorsunuz).

Kişisel duygular ne olursa olsun, doğru bir yöntem olmadığı için bu büyük ölçüde en az kötülük seçeneğidir.

Bu açık ve ortak bir deyim ve bunu tercih ediyorum, karşılaştırma sırasında yanlışlıkla bir değer atama şansı yok ve açıkça okuyor:

if(some_ptr){;}

Bu some_ptrbir işaretçi türü olduğunu biliyorsanız açıktır , ancak tamsayı karşılaştırması gibi de görünebilir:

if(some_ptr != 0){;}

Bu açıktır, yaygın durumlarda mantıklıdır ... Ama sızdıran bir soyutlama, NULLaslında 0gerçek anlamlıdır ve kolayca kötüye kullanılabilir:

if(some_ptr != NULL){;}

C ++ 0x şimdi açık ve doğru olduğu için tercih edilen yöntem olan nullptr'e sahiptir, sadece yanlışlıkla atama konusunda dikkatli olun:

if(some_ptr != nullptr){;}

C ++ 0x'e geçiş yapabilene kadar, bu yöntemlerden hangisini kullandığınız konusunda endişelenmenin zaman kaybı olduğunu iddia edeceğim, hepsi yetersizdir, bu yüzden nullptr icat edilmiştir (mükemmel yönlendirme ile ortaya çıkan genel programlama sorunları ile birlikte) .) En önemli şey tutarlılığı sağlamaktır.

C cinsinden

C farklı bir yaratıktır.

C NULL değerinde 0 veya ((void *) 0) olarak tanımlanabilir, C99, tanımlanmış null işaretçi sabitlerinin uygulanmasına izin verir. Yani aslında uygulamanın NULL tanımına gelir ve bunu standart kütüphanenizde incelemeniz gerekir.

Makrolar çok yaygındır ve genel olarak, dilde ve diğer şeylerde genel programlama desteğindeki eksiklikleri telafi etmek için çok kullanılırlar. Dil çok daha basittir ve ön işlemciye güvenmek daha yaygındır.

Bu açıdan muhtemelen NULLC'deki makro tanımını kullanmanızı tavsiye ederim .


tl; dr ve 0C ++ 'da haklı olmanıza rağmen , C:' de anlamı var (void *)0. tür hataları PITA olabileceğinden, C ++ ' 0da tercih edilir NULL.
Matt Joiner

@Matt: Ama NULLyine de sıfır.
GManNickG

@GMan: Sistemimde değil: #define NULL ((void *)0)fromlinux/stddef.h
Matt Joiner

@Matt: C ++ ile. 0'ın tercih edilebilir olduğunu, ancak NULLsıfır olması gerektiğini söylediniz .
GManNickG

Bu iyi bir nokta, bahsetmeyi ihmal ettim ve cevabımı önererek geliştiriyorum. ANSI C NULL ((void *) 0) olarak tanımlanmış olabilir, C ++ NULL değerini 0 olarak tanımlar. Bunun için standardı doğrudan trol etmedim ama benim anlayışım C ++ NULL'da 0 veya 0L olabilir.
M2tM

19

Kullanıyorum if (ptr), ama bu tartışmaya değmez.

Yolumu seviyorum çünkü kısa ve öz olsa da, diğerleri == NULLokumayı daha kolay ve daha açık hale getiriyor. Nereden geldiklerini görüyorum, sadece ekstra şeylerin bunu kolaylaştırdığını kabul etmiyorum. (Makrodan nefret ediyorum, bu yüzden önyargılıyım.) Size kalmış.

Tartışmanıza katılmıyorum. Koşullu ödevler için uyarı almıyorsanız, uyarı seviyelerinizi artırmanız gerekir. Bu kadar basit. (Ve iyi olan her şeyin aşkı için onları değiştirmeyin.)

C ++ 0x ile Not, yapabileceğimiz if (ptr == nullptr)bana hangi yapar okuma daha güzel. (Yine, makrodan nefret ediyorum. Ama nullptrgüzel.) Yine de hala if (ptr)alışkın olduğum için yapıyorum.


Umarım ekstra 5 yıllık deneyim fikrinizi değiştirdi :) C ++ / C operatörü if_exist () gibi bir şey olsaydı, o zaman mantıklı olurdu.
magulla

10

Açıkçası, neden önemli olduğunu anlamıyorum. Her ikisi de oldukça açıktır ve C veya C ++ ile orta derecede deneyimli herkes ikisini de anlamalıdır. Bir yorum olsa da:

Hatayı tanımayı planlıyorsanız ve işlevi yürütmeye devam etmemeyi planlıyorsanız (yani, bir istisna atayacak veya hemen bir hata kodu döndüreceksiniz), bir koruma yan tümcesi yapmalısınız:

int f(void* p)
{
    if (!p) { return -1; }

    // p is not null
    return 0;
}

Bu şekilde "ok kodundan" kaçınmış olursunuz.


Birisi burada kukuruku.co/hub/programming/i-do-not-know-cif(!p) tanımlanamayan davranış olduğunu gösterdi. Çalışabilir ya da çalışamaz.
David 天宇 Wong

@David 天宇 Wong bu bağlantıda örnek 2'den bahsediyorsan if(!p)tanımsız davranış değil, ondan önceki satır. Ve if(p==NULL)aynı problemi yaşayacaktı.
Mark Ransom

8

Şahsen her zaman kullandım if (ptr == NULL)çünkü niyetimi açıkça ortaya koyuyor, ama bu noktada sadece bir alışkanlık.

=Yerinde kullanmak ==, doğru uyarı ayarlarına sahip herhangi bir yetkili derleyici tarafından yakalanacaktır.

Önemli olan, grubunuz için tutarlı bir stil seçmek ve ona bağlı kalmaktır. Hangi yöne giderseniz gidin, sonunda buna alışacaksınız ve diğer insanların kodlarında çalışırken sürtünme kaybı kabul edilecektir.


7

foo == NULLUygulama lehine sadece bir nokta daha : foodiyelim ki, bir int *ya da bir bool *, o zaman if (foo)çek, bir okuyucu tarafından yanlışlıkla sivri kıymetin değerini test etmek olarak yorumlanabilir if (*foo). NULL karşılaştırma, bir işaretçi hakkında konuştuğumuzu hatırlatıyor.

Ama iyi bir isimlendirme sözleşmesinin bu argümanı tartıştığını düşünüyorum.


4

Aslında her iki varyantı da kullanıyorum.

İlk önce bir işaretçinin geçerliliğini kontrol ettiğiniz durumlar vardır ve NULL ise, bir işlevden geri dönersiniz / çıkarsınız. (Bunun "bir işlevin yalnızca bir çıkış noktası olması durumunda" tartışmasına yol açabileceğini biliyorum)

Çoğu zaman, işaretçiyi kontrol edin, sonra istediğinizi yapın ve ardından hata durumunu çözün. Sonuç, birden çok if ile çirkin x-kez girintili kod olabilir.


1
Şahsen ben bir çıkış noktası kullanarak sonuçlanacak ok kodundan nefret ediyorum. Cevabı zaten biliyorsam neden çıkmıyorum?
ruslik

1
@ruslik: C'de (ya da C ++ sınıflı C ++), birden çok döndürmeye sahip olmak temizlemeyi zorlaştırır. "Gerçek" C ++ 'da, temizliğiniz RAII tarafından ele alındığı için açıkça bir sorun değildir, ancak bazı programcılar (daha fazla veya daha az geçerli nedenlerden dolayı) geçmişte takılı kalır ve RAII'ye dayanmaz veya reddetmez veya izin verilmez .
jalf

@jalf bu gibi durumlarda gotoçok kullanışlı olabilir. Ayrıca pek çok durumda, malloc()temizlenmesi gereken a daha hızlı bir şekilde değiştirilebilir alloca(). Tavsiye edilmediğini biliyorum, ama bir nedenden dolayı varlar (bana göre, iyi adlandırılmış bir etiket gotogizlenmiş bir if/elseağaçtan çok daha temiz ).
ruslik

1
allocastandart dışı ve tamamen güvenli değildir. Başarısız olursa, programınız patlar. İyileşme şansı yoktur ve yığın nispeten küçük olduğundan, arıza olasılığı yüksektir. Başarısızlık durumunda, yığını yutturmanız ve ayrıcalıktan ödün vermeyen güvenlik açıkları sunmanız mümkündür. Tahsis edilecek boyut üzerinde küçük bir sınırınız allocaolmadığı sürece asla veya vlas kullanmayın (ve sonra normal bir dizi de kullanabilirsiniz).
R .. GitHub BUZA YARDIMCI DURDUR

4

C Programlama Dili (K&R), yanlışlıkla atamayı önlemek için null == ptr öğesini kontrol etmenizi sağlar.


23
K&R ayrıca "akıllı işaretçi nedir?" C ++ dünyasında işler değişiyor.
Alex Emelianov

2
ya da daha doğrusu, C ++ dünyasında işler zaten değişti ";)
jalf

3
K&R bunu (ref) nerede belirtiyor? Bu stili asla kendileri kullanmazlar. K & R2 bölüm 2'de if (!valid)yukarıda bile tavsiye edilir if (valid == 0).
schot

6
Oturun millet. K&R dedi, K&R değil. Baş harfleri büyük harfle yazılmadığı için daha az ünlüler.
jmucchiello

1
büyük harf kullanımı sizi yavaşlatır
Derek

3

Stil ve biçim incelemelerinizin bir parçası olacaksa, ölçmek için üzerinde anlaşmaya varılmış bir stil kılavuzu olmalıdır. Eğer varsa, stil rehberinin söylediklerini yapın. Bir tane yoksa, bunun gibi detaylar yazıldığı gibi bırakılmalıdır. Bu, zaman ve enerji kaybıdır ve kod incelemelerinin gerçekten ortaya çıkarması gereken şeylerden dikkati çeker. Cidden, bir stil rehberi olmadan, tercih ettiğim konvansiyonu kullanmasa bile, prensip olarak böyle bir kodu DEĞİŞTİRMEYE zorlayacağım.

Ve bu önemli değil, ama kişisel tercihim if (ptr). Anlam benim için her zamankinden daha açık if (ptr == NULL).

Belki de hata yolunu mutlu yoldan önce halletmenin daha iyi olduğunu söylemeye çalışıyor? Bu durumda hala gözden geçiren ile aynı fikirde değilim. Bunun için kabul edilmiş bir sözleşme olduğunu bilmiyorum, ama bence en "normal" koşul herhangi bir if ifadesinde ilk sırada yer almalıdır. Bu şekilde, işlevin ne hakkında olduğunu ve nasıl çalıştığını anlamak için daha az kazma yaptım.

Bunun istisnası, hata işlevden kefalet etmeme neden oluyorsa veya devam etmeden önce ondan kurtulabilirsem. Bu durumlarda, önce hatayı ele alıyorum:

if (error_condition)
  bail_or_fix();
  return if not fixed;

// If I'm still here, I'm on the happy path

Önde olağandışı durumla başa çıkarak, onunla ilgilenebilir ve sonra unutabilirim. Ben ön onu ele alarak mutlu yolda geri alamayan Ama eğer, o zaman işleneceğini sonra ana durumdan , çünkü kodu daha anlaşılır hale getirir. Bence.

Ama bir stil rehberinde değilse o zaman sadece benim görüşüm ve senin fikrin de geçerli. Ya standardize edin ya da etmeyin. Bir yorumcunun sadece bir fikri olduğu için sözde standardize olmasına izin vermeyin.


1

Bu, işaretçilerin boolC ++ ve C'de kontrol ifadesi olarak kullanılabilecek bir tür ve değere göre değerlendirdiği her iki dilin temellerinden biridir int.


1
  • İşaretçiler boolean değil
  • Modern C / C ++ derleyicileri if (foo = bar)yanlışlıkla yazdığınızda uyarı verir .

Bu yüzden tercih ederim

if (foo == NULL)
{
    // null case
}
else
{
    // non null case
}

veya

if (foo != NULL)
{
    // non null case
}
else
{
    // null case
}

Ancak, bir dizi stil kılavuzu yazıyor olsaydım buna böyle şeyler koymazdım, şöyle şeyler koyardım:

İmleci boş olarak kontrol ettiğinizden emin olun.


2
İşaretçilerin boolean olmadığı doğrudur, ancak C'de ifadeler boolean almazlar: tamsayı ifadeleri alırlar.
Ken

@Ken: Çünkü C bu açıdan kırılmış. Kavramsal olarak, bu bir boole ifadesidir ve (bence) bu şekilde ele alınmalıdır.
JeremyP

1
Bazı dillerde yalnızca null / not null için test yapan if ifadeleri bulunur. Bazı dillerde yalnızca işaret için bir tamsayıyı test eden if ifadeleri bulunur (3 yollu bir test). C'yi "kırmak" diye düşünmek için hiçbir neden göremiyorum çünkü sevdiğiniz farklı bir konsepti seçtiler. C hakkında nefret ettiğim pek çok şey var ama bu sadece C program modelinin zihinsel modelimle aynı olmadığı anlamına geliyor, ikimizden birinin (ben veya C) kırılmadığı anlamına gelmiyor.
Ken

@Ken: Bir boole kavramsal olarak bir sayı veya işaretçi değildir , Hangi dili boş verin .
JeremyP

1
Bir booleanın bir sayı veya işaretçi olduğunu söylemedim. Bir if ifadesinin yalnızca bir boole ifadesi alması veya alabilmesi için ısrar etmek için bir neden olmadığını ve eldeki ifadeye ek olarak karşı örnekler de sunduğunu söyledim. Birçok dil, yalnızca C'nin "sıfır / sıfır olmayan" şekilde değil, bir boole dışında bir şey alır. Hesaplamada, (yalnızca) bir boole kabul eden bir if-ifadesine sahip olmak nispeten yeni bir gelişmedir.
Ken

1

C / C ++ boolean içinde koşullarda türlerini kontrol etmez gerçeğinin büyük bir hayranıyım if, forve whiletabloların. Her zaman aşağıdakileri kullanıyorum:

if (ptr)

if (!ptr)

tamsayılarda veya boole dönüştürülen diğer türlerde bile:

while(i--)
{
    // Something to do i times
}

while(cin >> a >> b)
{
    // Do something while you've input
}

Bu tarzda kodlama benim için daha okunaklı ve daha net. Sadece benim kişisel fikrim.

Son zamanlarda, OKI 431 mikrodenetleyici üzerinde çalışırken, aşağıdakileri fark ettim:

unsigned char chx;

if (chx) // ...

daha verimli

if (chx == 1) // ...

çünkü daha sonraki bir durumda derleyici chx değerini 1 ile karşılaştırmak zorundadır. Burada chx sadece doğru / yanlış bayraktır.


1

Kullandığım çoğu derleyici en azından ifsözdizimi şekeri olmadan ödev konusunda uyarır , bu yüzden bu argümanı satın almam. Bununla birlikte, hem profesyonel olarak kullandım hem de hiçbir tercihim yok. == NULLBence olsa kesinlikle nettir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.