C veya C ++ 'da “boş bir kontrol” yapmak ne demektir?


21

C ++ 'ı öğrendim ve null değerini anlamakta zorlanıyorum. Özellikle, okuduğum dersler "boş onay" yapmaktan bahseder, ancak bunun ne anlama geldiğinden ya da neden gerekli olduğundan emin değilim.

  • Tam olarak null nedir?
  • "Boşluğu kontrol et" ne demek?
  • Her zaman boşluğu kontrol etmem gerekir mi?

Herhangi bir kod örneği çok takdir edilecektir.



Okuduğunuz herkes boş laflar hakkında hiç açıklama yapmadan ve örnek kod vermeden konuşurlarsa, daha iyi dersler almanızı öneririm ...
underscore_d

Yanıtlar:


26

C ve C ++ 'da, işaretçiler doğal olarak güvensizdir, yani bir göstergeye başvurduğunuzda, geçerli bir yere işaret ettiğinden emin olmak sizin sorumluluğunuzdadır; bu, "manuel bellek yönetimi" nin ne anlama geldiğinin bir parçasıdır (Java, PHP veya .NET çalışma zamanı gibi dillerde uygulanan, önemli çaba göstermeden geçersiz referanslar oluşturmanıza izin vermeyen otomatik bellek yönetimi şemalarının aksine).

Birçok hata yakalayan yaygın bir çözüm, hiçbir şeye işaret etmeyen tüm işaretleyicileri NULL(veya doğru C ++ 'da 0) ayarlamak ve imlece erişmeden önce bunu kontrol etmektir. Spesifik olarak, tüm işaretçilerinizi NULL olarak başlatmak (bunları bildirirken onları işaret edecek bir şeyiniz yoksa) ve bunları siz deleteveya free()onlardan sonra NULL olarak ayarlamak (bundan hemen sonra kapsam dışına çıkmazlarsa) yaygın bir uygulamadır . Örnek (C'de, aynı zamanda geçerli C ++):

void fill_foo(int* foo) {
    *foo = 23; // this will crash and burn if foo is NULL
}

Daha iyi bir sürüm:

void fill_foo(int* foo) {
    if (!foo) { // this is the NULL check
        printf("This is wrong\n");
        return;
    }
    *foo = 23;
}

Boş denetim olmadan, NULL imleci bu işleve sokmak segfault'a neden olur ve yapabileceğiniz hiçbir şey yoktur - işletim sistemi işleminizi basitçe öldürür ve belki bir çekirdek dökümü veya bir kilitlenme raporu iletişim kutusu açar. Boş denetim yerinde olduğunda, uygun hata işlemeyi gerçekleştirebilir ve incelikle düzeltebilirsiniz - sorunu kendiniz düzeltin, geçerli işlemi iptal edin, bir günlük girişi yazın, kullanıcıyı bilgilendirin, ne olursa olsun.


3
@ MrLister ne demek istiyorsun, boş kontroller C ++ 'da çalışmıyor mu? İşaretçiyi sıfırladığınızda boş bırakmanız gerekir.
TZHX

1
Demek istediğim, imleci NULL olarak ayarlamayı hatırlamalısın yoksa işe yaramazsa. Ve hatırlarsanız, başka bir deyişle , işaretçinin NULL olduğunu biliyorsanız , yine de fill_foo işlevine gerek duymazsınız. fill_foo, işaretçinin geçerli bir değeri olmadığını, işaretçinin bir değeri olup olmadığını kontrol eder . C ++ 'da, işaretçilerin NULL değerinin geçerli bir değeri olduğu garanti edilmez.
Bay Lister,

4
Bir assert () burada daha iyi bir çözüm olacaktır. "Güvenli olmaya" çalışmanın bir anlamı yok. Eğer NULL girdi ise, bu kesinlikle yanlış, peki neden programcının tamamen farkında olmasını sağlamak için açıkça çarpışmıyorsunuz? (Ve üretimde, farketmez, çünkü kimsenin NULL ile fill_foo () 'ı arayamayacağını kanıtladınız , değil mi? Gerçekten, bu o kadar da zor değil.)
Ambroz Bizjak

7
Bu işlevin daha iyi bir sürümünün, işaretçiler yerine referansları kullanması gerektiğini ve NULL denetiminin kullanılmayacağını belirtmeyi unutmayın.
Doktor Brown,

4
Bu, manuel bellek yönetiminin konusu değildir ve yönetilen bir program da havaya uçurur (ya da en azından, çoğu dilde olduğu gibi bir programın istisnasını kaldırın), boş bir referanstan vazgeçmeyi denerseniz.
Mason Wheeler

7

Diğer cevaplar tam olarak sorunuzu kapsıyordu. Aldığınız imlecin geçerli bir örnek örneğini (nesneler, ilkeller, vb.) İşaret ettiğinden emin olmak için boş bir denetim yapılır.

Yine de buraya kendi tavsiyemi ekleyeceğim. Boş kontrollerden kaçının. :) Boş kontroller (ve diğer Savunma Programlarının diğer biçimleri) kodları karıştırır ve aslında diğer hata işleme tekniklerinden daha fazla hataya açık hale getirir.

Nesne işaretçiler söz konusu olduğunda en sevdiğim teknik, Boş Nesne düzenini kullanmaktır . Bu, null yerine boş (imleç - ya da daha iyisi, a) boş bir diziye ya da listeye geri dönmek ya da null yerine boş bir dize ("") ya da "0" (ya da "hiçbir şeye eşdeğer bir şey) döndürmek anlamına gelir. "bağlamda) bir tamsayıya ayrıştırılmasını beklediğiniz yerde.

Bir bonus olarak, işte, 1965 yılında Algol W dili için CAR Hoare tarafından uygulanan (ilk olarak resmen) olan boş gösterici hakkında bilmediğiniz bir şey.

Milyar dolarlık hatam diyorum. 1965'teki boş referansın icadıydı. O zamanlar, nesne yönelimli bir dilde (ALGOL W) referanslar için ilk kapsamlı tip sistemi tasarlıyordum. Amacım, derleyici tarafından otomatik olarak yapılan kontrollerle referansların tüm kullanımının kesinlikle güvenli olmasını sağlamaktı. Ancak boş bir referans verme eğilimine karşı koyamadım, çünkü uygulanması çok kolaydı. Bu, son kırk yılda muhtemelen bir milyar dolarlık acı ve hasara neden olan sayısız hata, güvenlik açığı ve sistem çökmesine neden oldu.


6
Boş Nesne, yalnızca boş bir işaretçiye sahip olmaktan daha kötüdür. Eğer bir algoritma X sizde olmayan bir Y verisine ihtiyaç duyuyorsa, o zaman programınızda basitçe yaptığınız gibi sakladığınız bir hatadır .
DeadMG

Bağlama bağlıdır ve her iki yoldan da "veri varlığı" için yapılan testler kitabımdaki boşluğu boşa harcar. Tecrübelerime göre, eğer bir algoritma bir liste üzerinde çalışırsa, bir liste ve liste boşsa, o zaman algoritmanın yapması gereken hiçbir şey yoktur ve sadece for / foreach gibi standart kontrol ifadeleri kullanarak bunu başarır.
Yam Marcovic

Algoritmanın yapacak bir şeyi yoksa, neden çağırıyorsun? İlk başta aramak isteyebilecek olmanızın nedeni, önemli bir şey yapmasıdır .
DeadMG

@DeadMG Programlar girdilerle ilgili olduğu ve gerçek dünyada ev ödevlerinin aksine girdiler önemsiz olabilir (örn. Boş). Kod hala iki şekilde de aranıyor. İki seçeneğiniz vardır: ya uygunluğu (ya da boşluğu) kontrol edersiniz ya da algoritmaları, koşullu ifadeleri kullanarak alaka düzeyini açıkça kontrol etmeden okuyup iyi çalışacak şekilde tasarlarsınız.
Yam Marcovic

Buraya neredeyse aynı yorumu yapmaya geldim, o yüzden sana oyumu verdim. Bununla birlikte, bunun daha büyük bir zombi nesnesi sorununu da temsil ettiğini de ekleyeceğim - ne zaman çok aşamalı başlangıç ​​(veya yıkım) olan ve tamamen canlı olmayan, ancak tam olarak ölü olmayan nesneleriniz varsa. Belirli bir sonlandırma yapılmayan dillerde "güvenli" kod gördüğünüzde, nesnenin atılıp atılmadığını görmek için her fonksiyona kontroller ekleyen bu, genel sorun budur. Asla boş değilse, yaşamları boyunca ihtiyaç duydukları nesnelere sahip durumlarla çalışmalısınız.
ex0du5

4

Boş gösterici değeri, iyi tanımlanmış bir “hiçbir yerde” temsil eder; Eşit olmayan diğer herhangi bir işaretçi değeri ile karşılaştırmak için garanti edilen geçersiz bir işaretçi değeri. Boş bir işaretçiyi serbest bırakmaya çalışmak tanımsız davranışa neden olur ve genellikle çalışma zamanı hatasına neden olur; bu nedenle, işaretçinin düzenlemeyi kaldırmadan önce NULL olmadığından emin olmak istersiniz. Bir dizi C ve C ++ kütüphane işlevi, bir hata durumunu belirtmek için bir boş gösterici ile döner. Örneğin, mallocistenen bayt sayısını tahsis edemezse , kütüphane işlevi boş bir işaretçi değeri döndürür ve bu işaretçi ile belleğe erişmeye çalışmak (genellikle) bir çalışma zamanı hatasına neden olur:

int *p = malloc(sizeof *p * N);
p[0] = ...; // this will (usually) blow up if malloc returned NULL

Bu nedenle malloc, pNULL değerinin değerini kontrol ederek aramanın başarılı olduğundan emin olmalıyız :

int *p = malloc(sizeof *p * N);
if (p != NULL) // or just if (p)
  p[0] = ...;

Şimdi, bir dakika çoraplarını giy, bu biraz inişli çıkış olacak.

Boş bir işaretçi değeri ve bir boş işaretçi sabiti vardır ve ikisinin de aynı olması gerekmez. Boş işaretçi değeri , temel alınan mimarinin "hiçbir yerde" temsil etmek için kullandığı değerdir. Bu değer 0x00000000 veya 0xFFFFFFFF veya 0xDEADBEEF veya tamamen farklı bir şey olabilir. Boş gösterici değerinin her zaman 0 olduğunu varsaymayın .

Boş gösterici sabiti OTOH, her zaman 0 değerli bir integral ifadedir. Bildiğim kadarıyla olarak kaynak kodu söz konusu olduğunda, 0 (ya da herhangi bir entegre sentezleme 0 değerlendirir) boş bir işaretçisini temsil eder. Hem C hem de C ++, NULL makrosunu null işaretçisi sabiti olarak tanımlar. Kodunuz derlendiğinde, boş işaretçi sabiti , oluşturulan makine kodunda uygun boş işaretçi değeri ile değiştirilecektir .

Ayrıca, NULL öğesinin olası geçersiz işaretçi değerlerinden yalnızca biri olduğunu unutmayın ; Bir otomatik işaretçi değişkeni açıkça başlatılmadan, örneğin

int *p;

İlk olarak değişkende depolanan değer belirsizdir ve geçerli veya erişilebilir bir hafıza adresine karşılık gelmeyebilir. Ne yazık ki, NULL olmayan bir işaretçi değerinin kullanılmaya çalışılmadan önce geçerli olup olmadığını söylemenin (taşınabilir) bir yolu yoktur. Bu nedenle, işaretçilerle uğraşıyorsanız, bunları beyan ederken açıkça NULL olarak başlatmanız ve aktif olarak hiçbir şeye işaret etmediklerinde NULL olarak ayarlamak iyi bir fikirdir.

Bunun C'de C ++ 'dan ziyade bir sorun olduğunu unutmayın; idiomatic C ++ bu kadar çok işaretçi kullanmamalı.


3

Birkaç yöntem var, hepsi de aynı şeyi yapıyor.

int * foo = NULL; // bazen NULL yerine 0x00 veya 0 veya 0L olarak ayarlanır

boş denetim (işaretçinin boş olup olmadığını kontrol edin), sürüm A

eğer (foo == NULL)

boş denetim, sürüm B

if (! foo) // NULL, 0 olarak tanımlandığından,! foo boş bir göstergeden bir değer döndürür

null kontrol, versiyon C

eğer (foo == 0)

Üçünden, gelecekteki geliştiricilere neyi kontrol etmeye çalıştığınızı açıkça söylediği için ilk kontrolü kullanmayı tercih ediyorum VE bu foo'nun bir işaretçi olmasını beklediğinizi açıkça gösteriyor.


2

Sen değil. Bir işaretleyiciyi C ++ 'da kullanmanın tek nedeni, açıkça boş işaretçilerin varlığını istemenizdir; Aksi takdirde, kullanımı anlamsal olarak daha kolay olan ve boş olmayanları garanti eden bir referans alabilirsiniz.


1
@James: çekirdek modunda 'yeni'?
Nemanja Trifunovic

1
@James: C ++ kodlayıcılarının önemli bir çoğunluğunun sahip olduğu yetenekleri temsil eden bir C ++ uygulaması. Bu, tüm C ++ 03 dil özelliklerini (hariç export) ve tüm C ++ 03 kitaplığı özelliklerini ve TR1'i ve iyi bir C ++ 11 yığınını içerir.
DeadMG

5
Ben yapmak dileği insanlar demek olmaz "referanslar olmayan null adlı garanti." Yapmazlar. Bir boş gösterici gibi bir boş başvuru oluşturmak kolaydır ve aynı şekilde yayılırlar.
mjfgates

2
@ Stargazer: Soru, sadece dil tasarımcılarının ve iyi uygulamaların önerdiği şekilde kullandığınız araçları kullandığınızda% 100 gereksizdir.
DeadMG

2
@DeadMG, gereksiz olup olmadığı önemli değil. Sen soruya cevap vermedi . Tekrar söyleyeceğim: -1.
riwalk

-1

NULL değerini kontrol etmezseniz, özellikle, bir yapıya işaretçi ise, bir güvenlik açığı ile karşılaşmış olabilirsiniz - NULL işaretçi zorunluluğu. NULL işaretçi düzenleme, saldırganın bilgisayarınızın denetimini ele geçirmesine izin verebilecek arabellek taşması, yarış durumu ... gibi başka ciddi güvenlik açıklarına neden olabilir.

Microsoft, Oracle, Adobe, Apple ... gibi birçok yazılım satıcısı, bu güvenlik açıklarını gidermek için yazılım düzeltme eki yayımlar. Her işaretçinin NULL değerini kontrol etmelisin :)

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.