Boş gösterici için neden sıfır adresi kullanılır?


121

C (veya bu konuda C ++ 'da), eğer sıfır değerine sahiplerse, işaretçiler özeldir: Hafızalarını serbest bıraktıktan sonra işaretçileri sıfıra ayarlamam tavsiye edilir, çünkü bu, işaretçiyi tekrar serbest bırakmanın tehlikeli olmadığı anlamına gelir; malloc'u çağırdığımda, bana belleği alamazsa sıfır değerine sahip bir işaretçi döndürür; Kullandığım if (p != 0)emin geçti işaretçiler vb geçerlidir yapmak için her zaman

Ancak bellek adresleme 0'dan başladığına göre, 0 diğerleri kadar geçerli bir adres değil mi? Böyle bir durumda 0, boş işaretçileri işlemek için nasıl kullanılabilir? Neden bunun yerine negatif bir sayı boş değil?


Düzenle:

Bir sürü iyi cevap. Kendi zihnimin yorumladığı şeklinde ifade edilen cevaplarda söylenenleri özetleyeceğim ve yanlış anlarsam topluluğun beni düzelteceğini umuyorum.

  • Programlamadaki diğer her şey gibi bu da bir soyutlamadır. Sadece bir sabit, gerçekten 0 adresiyle ilgili değil. C ++ 0x, anahtar kelimeyi ekleyerek bunu vurgular nullptr.

  • Bu bir adres soyutlaması bile değil, C standardı tarafından belirtilen sabittir ve derleyici, hiçbir zaman "gerçek" bir adrese eşit olmadığından emin olduğu ve 0 değilse diğer boş işaretçilerle eşit olduğu sürece onu başka bir sayıya çevirebilir. platform için kullanılacak en iyi değer.

  • İlk günlerde olduğu gibi bir soyutlama olmaması durumunda, 0 adresi sistem tarafından kullanılır ve programcıya sınırlar dışında.

  • Negatif sayı önerim biraz çılgınca bir beyin fırtınasıydı, kabul ediyorum. Adresler için işaretli bir tamsayı kullanmak, boş göstericiden (-1 veya her neyse) ayrı olarak, değer uzayının geçerli adresler oluşturan pozitif tamsayılar ve boşa harcanmış negatif sayılar arasında eşit olarak bölündüğü anlamına geliyorsa, biraz israftır.

  • Herhangi bir sayı her zaman bir veri türü ile gösterilebilirse, 0'dır (Muhtemelen 1 de olabilir. İşaretsizse 0 veya 1 olacak bir bitlik tamsayı veya imzalıysa sadece işaretli bit veya iki bitlik tamsayı düşünüyorum. [-2, 1] olacaktır. Ancak 0'ın boş ve 1'in bellekteki tek erişilebilir bayt olduğunu varsayabilirsiniz.)

Yine de zihnimde çözülmemiş bir şey var. Yığın Taşma sorusu Belirli bir sabit adrese işaretçi, boş işaretçi için 0 bir soyutlama olsa bile, diğer işaretçi değerlerinin zorunlu olmadığını söyler. Bu beni başka bir Stack Overflow sorusu göndermeme neden oluyor, sıfır adresine hiç erişmek isteyebilir miyim? .


11
Sen aynı kolaylıkla değişebilir if (p != 0)için if (p)size Java ayırabilir alışkanlık çıkmak gerekecek rağmen, C ve C ++ ortak bir deyim olan.
Paul Tomblin

14
Bir şeyi iki kez silmek, kodunuzun yanlış olduğu anlamına gelir. Daha sonra işaretçileri null olarak ayarlamamanızı tavsiye ederim, böylece sorunu bastırmak yerine çökebilir ve düzeltebilirsiniz. Her durumda, bir adresin bir tam sayı olduğunu varsayma hatasını yaparsınız. Bu mutlaka doğru değildir ve 0 yalnızca uygulamaya özgü bazı gerçek işaretçi değerlerini temsil eder . Kavramsal olarak "olumsuz adres" mantıklı değildir.
GManNickG

6
@GMan: Belki de işaretçiyi çökmeye zorlayacak bir adrese ayarlamak iyi bir fikir, örneğin 0xDEADBEEF.
Billy ONeal

5
Asla ölmeyecek soru!

8
@Noah: Nokta null olarak ayarlandı -> programlama hatalarını gizle, null olarak ayarlamayın -> programlama hatalarını bulun. Seni bilmiyorum ama kodumun doğru olmasını istiyorum.
GManNickG

Yanıtlar:


65

2 puan:

  • yalnızca kaynak koddaki 0 ​​sabit değeri boş göstericidir - derleyici uygulaması çalışan kodda istediği veya ihtiyaç duyduğu değeri kullanabilir. Bazı platformlar, uygulamanın boş gösterici olarak kullanabileceği 'geçersiz' özel bir işaretçi değerine sahiptir. C SSS'nin bir sorusu var, "Cidden, herhangi bir gerçek makine gerçekten sıfırdan farklı sıfır işaretçileri veya farklı türlere işaretçiler için farklı temsiller kullanıyor mu?" , bu, çalışma zamanında farklı şekilde temsil edilirken C kaynağında boş işaretçi olan bu 0 özelliğini kullanan birkaç platforma işaret eder. C ++ standardı, "sıfır değerine sahip bir integral sabit ifadeyi dönüştürmenin her zaman boş bir işaretçi verdiğini,

  • Negatif bir değer, platform tarafından bir adres kadar kullanılabilir olabilir - C standardı, boş göstericiyi belirtmek için kullanılacak bir şey seçmeliydi ve sıfır seçildi. Dürüst olmak gerekirse, diğer koruyucu değerlerin dikkate alınıp alınmadığından emin değilim.

Boş gösterici için tek gereksinimler şunlardır:

  • Eşit olmayan bir işaretçiyle gerçek bir nesneyi karşılaştırmak garantilidir
  • herhangi iki boş işaretçi eşit karşılaştırır (C ++ bunu, yalnızca aynı türdeki işaretçiler için tutması gerektiği şekilde rafine eder)

12
+1 0'ın yalnızca tarihsel nedenlerle seçildiğinden şüpheleniyorum. (0, çoğu zaman başlangıç ​​ve geçersiz bir adresdir.) Elbette genel olarak böyle bir varsayım her zaman doğru değildir, ancak 0 oldukça iyi çalışır.
GManNickG

8
Uzay da katkıda bulunan bir faktör olabilir. C'nin ilk geliştirildiği günlerde, bellek şimdi olduğundan ÇOK daha maliyetliydi. Sıfır sayısı, bir XOR komutu kullanılarak veya anlık bir değer yüklemeye gerek kalmadan uygun şekilde hesaplanabilir. Mimariye bağlı olarak, bu potansiyel olarak yerden tasarruf sağlayabilir.
Sparky

6
@GMan - Haklısın. İlk CPU'larda, sıfır bellek adresi özeldi ve çalışan yazılımdan erişime karşı donanım korumasına sahipti (bazı durumlarda bu, sıfırlama vektörünün başlangıcıydı ve bunu değiştirmek, CPU'nun sıfırlanmasını veya başlamasını engelleyebilirdi). Programcılar, bu donanım korumasını yazılımlarında bir hata algılama biçimi olarak kullandılar ve bunu yapmak için CPU talimatlarını harcamak yerine, CPU adresinin başlatılmamış veya geçersiz işaretçiler için kod çözme mantığı kontrolüne izin verdi. Sıfır adresinin amacı değişmiş olsa da, sözleşme bugüne kadar kalır.
bta

10
Minix 16 bit derleyici, NULL için 0xFFFF kullandı.
Joshua

3
Birçok gömülü sistemde 0 geçerli bir adrestir. -1 değeri (tümü bir bit) de geçerli bir adrestir. ROM'lar için sağlama toplamlarının, veriler 0 adresinde başladığında hesaplanması zordur. :-(
Thomas Matthews

31

Geçmişte, 0'dan başlayan adres alanı her zaman ROM'du ve bazı işletim sistemleri veya düşük seviyeli kesinti işleme rutinleri için kullanılıyordu, günümüzde her şey sanal olduğundan (adres alanı dahil), işletim sistemi herhangi bir adrese herhangi bir atamayı eşleyebilir, böylece özellikle adres 0'da hiçbir şey AYIRMAZ.


6
Bu hemen hemen öyle. Tarihsel geleneğe göre ve ilk adresler kesme işleyicileri için kullanıldı, bu nedenle normal programlar için kullanılamaz. Ayrıca, 0 "boştur", bu da değer yok / işaretçi yok şeklinde yorumlanabilir.
TomTom

15

IIRC, "boş işaretçi" değerinin sıfır olacağı garanti edilmez. Derleyici 0'ı sistem için uygun olan "boş" değerine çevirir (pratikte muhtemelen her zaman sıfırdır, ancak zorunlu değildir). Bir işaretçiyi sıfırla karşılaştırdığınızda aynı çeviri uygulanır. İşaretçileri yalnızca birbirleriyle ve bu özel değer 0 ile karşılaştırabildiğiniz için, programcıyı sistemin bellek temsili hakkında herhangi bir şey bilmesini engeller. Neden 42 yerine 0'ı seçtiklerine gelince, bunun nedeni çoğu programcının 0'da saymaya başlamasıdır :) (Ayrıca çoğu sistemde 0 ilk bellek adresidir ve uygun olmasını istediler çünkü Benim tarif ettiğim gibi çevirilerin alıştırması nadiren gerçekleşir; dil onlara izin verir).


5
@ Justin: Yanlış anladın. 0 sabiti her zaman boş göstericidir. @Meador'un söylediği, boş göstericinin (0 sabiti ile gösterilir) sıfır adresine karşılık gelmemesinin mümkün olduğudur. Bazı platformlarda, boş bir işaretçi ( int* p = 0) oluşturmak, değeri 0xdeadbeefveya tercih ettiği başka bir değeri içeren bir işaretçi oluşturabilir . 0 bir boş göstericidir, ancak bir boş gösterici mutlaka sıfırı adresleyen bir gösterici değildir. :)
jalf

NULL işaretçisi ayrılmış bir değerdir ve derleyiciye bağlı olarak herhangi bir bit modeli olabilir. NULL işaretçisi, 0 adresini gösterdiği anlamına gelmez.
Sharjeel Aziz

3
Ama @Jalf sabit 0 değil her zaman boş işaretçi. Derleyicinin platformun gerçek boş göstericisini bizim için doldurmasını istediğimizde yazdığımız şey budur . Pratik olarak, boş işaretçi genellikle yapar olsa, adres sıfır karşılık gelir ve bunun neden soran olarak Joel'in sorusunu yorumlamak. Sonuçta bu adreste sözde geçerli bir bellek baytı var, öyleyse neden geçerli bir baytı oyundan çıkarmak yerine var olmayan bir baytın varolmayan bir adresini kullanmayalım? (Joel'in düşündüğünü sandığım şeyi yazıyorum, kendime sorduğum bir soru değil.)
Rob Kennedy

@Rob: Sırala. Ne demek istediğini biliyorum ve haklısın, ama ben de öyleyim :) Sabit tamsayı 0, kaynak kodu seviyesindeki boş göstericiyi temsil eder. Boş göstericinin 0 ile karşılaştırılması doğru verir. Bir işaretçiye 0 atanması, o işaretçiyi boş olarak ayarlar. 0 olan boş işaretçi. Ancak bir boş göstericinin gerçek bellek içi temsili, sıfır bit modelinden farklı olabilir. (Her neyse, yorumum @ Joel'in sorusuna değil, @ Justin'in artık silinmiş yorumuna yanıt olarak geldi. :)
jalf

@jalf @Rob Açıklamak için bazı terimlere ihtiyacınız var, sanırım. :) §4.10 / 1'den: "Bir boş gösterici sabiti , sıfır olarak değerlendirilen tamsayı türünde bir integral sabit ifade rvalue'dur. Bir boş gösterici sabiti bir işaretçi türüne dönüştürülebilir; sonuç, bu türün boş işaretçi değeridir ve diğer tüm işaretçi değerlerinden nesneye veya işaretçi işlev türüne göre ayırt edilebilir. "
GManNickG

15

İşaretçi bağlamında sabit sıfırın anlamını yanlış anlıyor olmalısınız.

Ne C ne de C ++ işaretçilerinin "sıfır değeri" olamaz. İşaretçiler aritmetik nesneler değildir. "Sıfır" veya "negatif" gibi sayısal değerlere veya bu nitelikte herhangi bir şeye sahip olamazlar. Yani "işaretçiler ... sıfır değerine sahip" hakkındaki ifadeniz hiçbir anlam ifade etmiyor.

C & C ++ 'da işaretçiler, ayrılmış boş işaretçi değerine sahip olabilir . Boş işaretçi değerinin gerçek temsilinin herhangi bir "sıfır" ile ilgisi yoktur. Belirli bir platform için kesinlikle uygun herhangi bir şey olabilir. Çoğu platformda boş işaretçi değerinin fiziksel olarak gerçek bir sıfır adres değeri ile temsil edildiği doğrudur. Bununla birlikte, eğer bazı platform adreslerinde 0 gerçekten bir amaç için kullanılıyorsa (yani, 0 adresinde nesneler oluşturmanız gerekebilir), bu tür platformdaki boş işaretçi değeri büyük olasılıkla farklı olacaktır. Fiziksel olarak 0xFFFFFFFFadres değeri olarak veya0xBAADBAADÖrneğin .

Bununla birlikte, boş işaretçi değerinin belirli bir platformda nasıl temsil edildiğine bakılmaksızın, kodunuzda sabit olarak boş işaretçileri atamaya devam edeceksiniz 0. Belirli bir işaretçiye boş işaretçi değeri atamak için, gibi ifadeleri kullanmaya devam edeceksiniz p = 0. Ne istediğinizi gerçekleştirmek ve onu uygun boş işaretçi değer gösterimine çevirmek, yani adres değerini 0xFFFFFFFFişaretçiye yerleştirecek koda çevirmek derleyicinin sorumluluğundadır.p .

Kısacası, 0boş işaretçi değerleri oluşturmak için sıralama kodunuzda kullanmanız , boş işaretçi değerinin bir şekilde adrese bağlı olduğu anlamına gelmez 0. 0Eğer kaynak kodunda kullandıkları boş-işaretçi değeri için "işaret" asıl fiziksel adrese kesinlikle hiçbir ilişkisi olmayan bir sadece "sözdizimsel şeker" dir.


3
<quote> İşaretçiler aritmetik nesneler değildir </quote> İşaretçi aritmetiği C ve C ++ 'da oldukça iyi tanımlanmıştır. Gerekliliğin bir kısmı, her iki göstericinin de aynı kompoziti işaret etmesidir. Boş gösterici herhangi bir bileşimi göstermez, bu nedenle onu işaretçi aritmetik ifadelerinde kullanmak yasadışıdır. Örneğin bunun garantisi yoktur (p1 - nullptr) - (p2 - nullptr) == (p1 - p2).
Ben Voigt

5
@Ben Voigt: Dil özelliği aritmetik tip kavramını tanımlar . Tüm söylediğim, işaretçi türlerinin aritmetik türler kategorisine ait olmadığıdır. İşaretçi aritmetiği , tamamen dilsel bir tesadüf olan farklı ve tamamen alakasız bir öyküdür.
AnT

1
Aritmetik nesneleri okuyan biri, bunun "aritmetik operatörler anlamında" (birkaçı işaretçilerde kullanılabilir) veya "işaretçi aritmetiği anlamında" değil "aritmetik türler anlamında" anlamına geldiğini nasıl bilebilir? Dilsel tesadüfler söz konusu olduğunda, aritmetik nesnenin aritmetik türlerden çok işaretçi aritmetiği ile ortak olan daha fazla harfi vardır . Aynı zamanda, standart işaretçi değeri hakkında konuşur . Orijinal poster muhtemelen işaretçi değerinden ziyade bir işaretçinin tamsayı gösterimini ifade ediyordu ve açıkça 0 ile temsil edilmesine gerek yoktu.NULL
Ben Voigt

Örneğin, C / C ++ terminolojisindeki skaler nesneler terimi , skaler tipteki nesnelerin kısaltmasıdır (aynı POD nesneleri = POD türlerinin nesneleri ). Aritmetik nesneler terimini tamamen aynı şekilde kullandım, yani aritmetik türdeki nesneler . "Birinin" bunu bu şekilde anlamasını bekliyorum. Her zaman açıklama isteyemeyen biri.
AnT

1
(donanım söz konusu olduğunda) null
değerinin

8

Ancak bellek adresleme 0'dan başladığına göre, 0 diğerleri kadar geçerli bir adres değil mi?

Bazı / birçok / tüm işletim sistemlerinde, bellek adresi 0 bir şekilde özeldir. Örneğin, genellikle geçersiz / var olmayan belleğe eşlenir, bu da erişmeye çalıştığınızda bir istisnaya neden olur.

Neden bunun yerine negatif bir sayı boş değil?

İşaretçi değerlerinin tipik olarak işaretsiz sayılar olarak ele alındığını düşünüyorum: aksi takdirde, örneğin 32 bitlik bir işaretçi 4 GB yerine yalnızca 2 GB belleği adresleyebilir.


4
Sıfır adresinin geçerli bir adres olduğu ve bellek korumasının olmadığı bir cihazı kodladım. Null işaretçiler aynı zamanda hepsi bit sıfırdı; yanlışlıkla bir boş göstericiye yazdıysanız, sıfır adresteki işletim sistemi ayarlarının üzerinden geçtiniz; neşe genellikle ortaya çıkmadı.
MM

1
Evet: korumasız modda bir x86 CPU'da, örneğin, adres 0, kesme vektör tablosudur .
ChrisW

@ChrisW: Korumasız mod x86'da, özellikle sıfır adresi sıfıra bölme vektörüdür, bazı programların yazmak için tamamen geçerli nedenleri olabilir.
supercat

Kullanılabilir depolamanın fiziksel adres, sıfırdan başlayacağı platformlarda bile, bir C uygulaması ya adresi hiç alınmayan bir nesneyi tutmak için kolayca sıfır adresini kullanabilir ya da basitçe belleğin ilk sözcüğünü kullanılmadan bırakabilir. Çoğu platformda, sıfırla karşılaştırma, bir talimatı başka herhangi bir şeyle karşılaştırmaya karşı kaydeder, bu nedenle ilk depolama kelimesini boşa harcamak bile boş için sıfır olmayan bir adres kullanmaktan daha ucuz olacaktır. C Standardı tarafından kapsanmayan şeylerin adreslerinin (örneğin, G / Ç bağlantı noktaları veya kesinti vektörleri) eşit olmayan ile sıfır arasında olması veya bunun ...
supercat

... sistem süreci boş işaretçi diğerlerinden farklı olarak erişir, bu nedenle tüm-bit-sıfır, fiziksel konum sıfıra erişimin yararlı ve anlamlı olacağı sistemlerde bile genellikle "boş" için iyi bir adrestir.
supercat

5

Tahminim, daha az talimatla test edilebildiğinden, geçersiz bir işaretçi tanımlamak için sihirli değer 0'ın seçildiği olacaktır. Bazı makine dilleri, yazmaçları yüklerken verilere göre otomatik olarak sıfır ve işaret bayraklarını ayarlar, böylece daha sonra basit bir yükle boş gösterici için test edebilir ve ayrı bir karşılaştırma talimatı yapmadan dal komutlarını kullanabilirsiniz.

(Çoğu ISA yalnızca ALU komutlarına bayrak koyar, yüklere değil. Ve genellikle C kaynağını ayrıştırırken derleyici dışında, hesaplama yoluyla işaretçiler üretmezsiniz. en azından, karşı karşılaştırın.)

İlk çalıştığım makineler olan Commodore Pet, Vic20 ve C64'te RAM, konum 0'da başladı, bu nedenle gerçekten isterseniz boş bir işaretçi kullanarak okumak ve yazmak tamamen geçerliydi.


3

Sanırım bu sadece bir kongre. Geçersiz bir işaretçiyi işaretlemek için bazı değerler olmalıdır.

Sadece bir baytlık adres alanını kaybedersiniz, bu nadiren sorun olur.

Olumsuz işaret yok. İşaretçiler her zaman işaretsizdir. Ayrıca olumsuz olabilirlerse, kuralınız adres alanının yarısını kaybedeceğiniz anlamına gelir.


Not: Adres alanını gerçekten kaybetmezsiniz; Eğer yaparak adrese 0 işaretçisi edinebilirsiniz: char *p = (char *)1; --p;. Boş gösterici üzerindeki davranış standart tarafından tanımlanmadığı için, bu sistem paslında 0 adresini okuyup yazabilir, adres vermek için artış 1, vb.
MM

@MattMcNabb: Sıfır adresinin geçerli bir donanım adresi olduğu bir uygulama, sıfır adresini okuma ve bu değeri x'e kaydetme davranışını mükemmel şekilde yasal olarak tanımlayabilirchar x = ((char*)0); . Bu tür bir kod, davranışını tanımlamayan herhangi bir uygulamada Tanımsız Davranışa neden olur, ancak bir standardın bir şeyin Tanımlanmamış Davranış olduğunu söylediği gerçeği, uygulamaların yapacağı şey için kendi özelliklerini sunmasını hiçbir şekilde yasaklamaz.
supercat

@supercat ITYM *(char *)0. Bu doğru, ancak benim önerime göre gerçeklemenin *(char *)0diğer boş gösterici işlemlerinin davranışını veya davranışını tanımlamasına gerek yok .
MM

1
@MattMcNabb: Davranışı, char *p = (char*)1; --p;yalnızca bir nesnenin ilk baytından başka bir şeye işaretçi bir nesneye dönüştürüldükten sonra gerçekleştirilmişse intptr_tve bu dönüştürmenin sonucu 1 değerini verdiyse standart tarafından tanımlanacaktır. ve bu özel durumda bir sonucu --pdöküm zaman, işaretçi değeri bir önceki bayta bir işaretçiyi doğuracak intptr_t, elde edildiğini 1.
süper araba

3

C, boş göstericiyi temsil etmek için 0 kullansa da, göstericinin değerinin sıfır olamayacağını unutmayın. Bununla birlikte, çoğu programcı yalnızca boş göstericinin aslında 0 olduğu sistemleri kullanır.

Ama neden sıfır? Her sistemin paylaştığı bir adres. Ve çoğu zaman düşük adresler işletim sistemi amaçları için ayrılmıştır, bu nedenle değer uygulama programlarına sınır dışı olarak çalışır. Bir işaretçiye yanlışlıkla bir tamsayı değeri atanması, başka herhangi bir şey gibi sıfıra düşme olasılığındadır.


3
Tüm bunların ardındaki daha olası neden şudur: sıfıra önceden başlatılmış belleği dağıtmak ucuzdur ve bu bellekteki değerlerin tamsayı 0, kayan nokta 0.0 ve boş işaretçiler gibi anlamlı bir şeyi temsil etmesi uygundur. C'deki sıfır / boş olarak başlatılan statik verilerin yürütülebilir dosyada herhangi bir yer kaplaması gerekmez ve yüklendiğinde sıfır dolu bir bloğa eşlenir. Sıfır, makine dillerinde de özel bir muamele görebilir: "sıfıra eşitse dallanma" gibi kolay sıfır karşılaştırmaları, vb. MIPS, sadece sıfır sabit olan bir kukla yazmacıya bile sahiptir.
Kaz

2

Tarihsel olarak, bir uygulamanın düşük belleği sistem kaynakları tarafından işgal ediliyordu. O günlerde sıfır, varsayılan boş değer haline geldi.

Bu, modern sistemler için mutlaka doğru olmasa da, bellek ayırmanın size sağladığı şey dışında herhangi bir şeye işaretçi değerleri ayarlamak yine de kötü bir fikirdir.


2

Bir işaretçiyi sildikten sonra boş değer olarak ayarlamama argümanına gelince, böylece gelecekte "hataları açığa çıkar" ...

Bu konuda gerçekten, gerçekten endişeliyseniz, çalışması garantili olan daha iyi bir yaklaşım assert () 'den yararlanmaktır:


...
assert(ptr && "You're deleting this pointer twice, look for a bug?");
delete ptr;
ptr = 0;
...

Bu biraz fazladan yazma ve hata ayıklama yapıları sırasında fazladan bir kontrol gerektirir, ancak size istediğinizi vereceği kesindir: ptr 'iki kez' silindiğinde dikkat edin. Yorum tartışmasında verilen alternatif, bir çökme yaşamanız için işaretçiyi boşa ayarlamamak, kesinlikle başarılı olacağı garanti edilmez. Daha kötüsü, yukarıdakilerden farklı olarak, bu "böceklerden" biri rafa ulaşırsa kullanıcıda bir çökmeye (veya çok daha kötü) neden olabilir. Son olarak, bu sürüm gerçekte ne olduğunu görmek için programı çalıştırmaya devam etmenizi sağlar.

Bunun sorulan soruyu yanıtlamadığının farkındayım, ancak yorumları okuyan birinin, işaretçileri ücretsiz olarak gönderilmesi mümkünse () veya iki kez silin. Mümkün olduğu bu birkaç durumda, Tanımsız Davranışı bir hata ayıklama aracı olarak kullanmak ASLA iyi bir uygulama değildir. Sonunda geçersiz bir işaretçiyi silmenin neden olduğu bir hatayı bulmak zorunda kalan hiç kimse bunu önermez. Bu tür hataların araştırılması saatler alır ve neredeyse her zaman programı tamamen beklenmedik bir şekilde etkiler ve orijinal soruna geri dönmenin zor ya da imkansız olduğu.


2

Birçok işletim sisteminin sıfır işaretçi gösterimi için tüm bitleri sıfır kullanmasının önemli bir nedeni, bunun anlamı memset(struct_with_pointers, 0, sizeof struct_with_pointers)ve benzerinin içerideki tüm işaretçileri struct_with_pointersboş işaretçiler olarak ayarlamasıdır. Bu, C standardı tarafından garanti edilmez, ancak birçok program bunu varsayar.


1

Eski DEC makinelerinden birinde (PDP-8, sanıyorum), C çalışma zamanı belleğin ilk sayfasını bellek koruyacaktı, böylece bu bloktaki belleğe erişme girişimleri bir istisnanın ortaya çıkmasına neden olacaktı.


PDP-8'in C derleyicisi yoktu. PDP-11'in bellek koruması yoktu ve VAX, sessizce 0'dan NULL işaretçi ayrıştırmalarına geri dönmesiyle ünlüydü. Bunun hangi makineye atıfta bulunduğundan emin değilim.
fuz

1

Sentinel değerinin seçimi gelişigüzeldir ve aslında bu, C ++ 'ın bir sonraki sürümü tarafından (gayri resmi olarak "C ++ 0x" olarak bilinir, büyük olasılıkla gelecekte ISO C ++ 2011 olarak bilinir) ele alınmaktadır. nullptrboş değerli bir göstericiyi temsil eden anahtar kelime . C ++ 'da, 0 değeri, herhangi bir POD ve varsayılan kurucuya sahip herhangi bir nesne için bir başlatma ifadesi olarak kullanılabilir ve bir işaretçi başlatma durumunda sentinel değeri atamanın özel bir anlamı vardır. Negatif bir değerin neden seçilmediğine gelince, adresler genellikle 0 ile 2 arasındadır. N arasındadır.Bazı N değeri için -1. Diğer bir deyişle, adresler genellikle işaretsiz değerler olarak kabul edilir. Eğer maksimum değer sentinel değer olarak kullanılmışsa, bellek boyutuna bağlı olarak sistemden sisteme değişmesi gerekirken, 0 her zaman gösterilebilir bir adrestir. Bellek adresi 0 tipik olarak programlarda kullanılamadığından ve günümüzde çoğu işletim sistemi, çekirdeğin bölümlerini belleğin alt sayfalarına yüklediğinden ve bu tür sayfalar tipik olarak bir program tarafından dokunulduğunda (başvurulan) (çekirdeği kaydedin) bir hataya neden olur.


1

Bir değeri olmalı. Açıkçası, kullanıcının yasal olarak kullanmak isteyebileceği değerlere adım atmak istemezsiniz. C çalışma zamanı sıfır başlatılmış veriler için BSS segmenti sağladığından, sıfırı başlatılmamış bir işaretçi değeri olarak yorumlamanın belirli bir anlam ifade ettiğini tahmin ediyorum.


0

Nadiren bir işletim sistemi, 0 adresine yazmanıza izin verir. İşletim sistemine özgü şeyleri düşük bellekte tutmak yaygındır; yani, IDT'ler, sayfa tabloları, vb. (Tablolar RAM'de olmalıdır ve bunları en altına yapıştırmak, RAM'in tepesinin nerede olduğunu denemek ve belirlemek için daha kolaydır.) Ve aklı başında hiçbir işletim sistemi size izin vermez. sistem tablolarını willy-nilly düzenleyin.

Bu, K & R'nin C'yi yaptıklarında aklına gelmemiş olabilir, ancak (0 == null'un hatırlanması oldukça kolay olduğu gerçeğiyle birlikte), 0'ı popüler bir seçim yapar.


Bu korumalı modda doğru değildir ve aslında bazı Linux yapılandırmalarında, sen yapabilirsiniz sanal adrese 0. yazma
L̳o̳̳n̳̳g̳̳p̳o̳̳k̳̳e̳̳

0

Değer 0, belirli ifadelerde çeşitli anlamlar alan özel bir değerdir. İşaretçiler söz konusu olduğunda, birçok kez belirtildiği gibi, muhtemelen o zamanlar "varsayılan sentinel değeri buraya ekle" demenin en uygun yolu olduğu için kullanılır. Sabit bir ifade olarak, bir işaretçi ifadesi bağlamında bitsel sıfır (yani tüm bitler sıfıra ayarlanmış) ile aynı anlama sahip değildir. C ++ 'da, NULLişaretçi üyesi ve üye işlevine işaretçi gibi bit düzeyinde sıfır temsiline sahip olmayan birkaç tür vardır .

Neyse ki, C ++ 0x "da ayrılmaz ifadeler için sıfır Bitsel eşleşmiyor bilinen geçersiz işaretçi anlamı ifade" için yeni bir anahtar kelime vardır: nullptr. C ++ ile hedefleyebileceğiniz, engelleme olmadan 0 adresinin referansının kaldırılmasına izin veren birkaç sistem olsa da, programcı dikkatli olun.


0

Bu ileti dizisinde zaten pek çok iyi yanıt var; 0Boş işaretçilerin değerini tercih etmenin muhtemelen birçok farklı nedeni vardır , ancak iki tane daha ekleyeceğim:

  • C ++ 'da, bir göstericinin sıfır başlatılması onu null olarak ayarlayacaktır.
  • Birçok işlemcide 0'a bir değer ayarlamak veya 0'a eşit / sıfıra eşit olduğunu test etmek diğer sabitlere göre daha etkilidir.

0

Bu, C / C ++ 'daki işaretçilerin uygulanmasına bağlıdır. Bir işaretçiye yapılan atamalarda NULL'un eşdeğer olmasının belirli bir nedeni yoktur.


-1

Bunun tarihsel nedenleri var, ancak bunun da optimizasyon nedenleri var.

İşletim sisteminin, 0'a başlatılmış bellek sayfalarına sahip bir işlem sağlaması yaygındır. Bir program bu bellek sayfasının bir bölümünü bir işaretçi olarak yorumlamak isterse, o zaman 0'dır, bu nedenle programın bu işaretçinin olduğunu belirlemesi yeterince kolaydır. Başlatılmadı. (bu, başlatılmamış flash sayfalara uygulandığında o kadar iyi çalışmaz)

Diğer bir neden de, birçok işlemcide bir değerin 0'a eşdeğerliğini test etmenin çok kolay olmasıdır. Bu bazen ekstra talimatlar gerekmeden yapılan ücretsiz bir karşılaştırmadır ve genellikle başka bir kayıtta sıfır değeri girmeye gerek kalmadan yapılabilir veya karşılaştırılacak talimat akışında değişmez olarak.

Çoğu işlemci için ucuz karşılaştırmalar, 0'dan küçük ve 0'a eşit olan işaretlerdir (0'dan büyük ve 0'a eşit olmayan işaretler, her ikisi tarafından ima edilmektedir)

Olası tüm değerlerden 1 değerinin kötü veya ilklendirilmemiş olarak ayrılması gerektiğinden, onu kötü değere eşdeğerlik için en ucuz teste sahip olanı da yapabilirsiniz. Bu aynı zamanda '\ 0' ile sonlandırılmış karakter dizileri için de geçerlidir.

Bu amaçla 0'dan büyük veya küçük kullanmayı denerseniz, adres aralığını ikiye bölerdiniz.


-2

Sabit 0yerine kullanılan NULLC yıl önce bazı cavemen trilyonlarca tarafından yapıldı çünkü NULL, NIL, ZIP, veya NADDAdaha tüm yapılmış çok daha mantıklı olurdu 0.

Ancak bellek adresleme 0'dan başladığına göre, 0 diğerleri kadar geçerli bir adres değil mi?

Aslında. Pek çok işletim sistemi, sanal bir adres alanında bile, adres sıfırdaki herhangi bir şeyi eşlemenizi engellese de (insanlar C'nin güvenli olmayan bir dil olduğunu fark ettiler ve boş işaretçi özendirme hatalarının çok yaygın olduğunu düşünerek, bunları engelleyerek "düzeltmeye" karar verdiler. 0 sayfasına eşlenecek kullanıcı alanı kodu; Bu nedenle, bir geri arama çağırırsanız ancak geri arama işaretçisi NULL ise, rastgele bir kod yürütmek zorunda kalmazsınız).

Böyle bir durumda 0, boş işaretçileri işlemek için nasıl kullanılabilir?

Çünkü 0bir işaretçi göre kullanılan bazı ile değiştirilir uygulama belirli bir Malloc failure malloc'un dönüş değeri değeri.

Neden bunun yerine negatif bir sayı boş değil?

Bu daha da kafa karıştırıcı olurdu.


"Mağara adamları" vb. Hakkındaki düşünceniz, muhtemelen, ayrıntıların farklı olduğunu düşünüyorum. C'ye evrimleşenlerin en eski biçimleri, intyalnızca bir işaretçi ile aynı boyutta olmayan belirli bir mimari üzerinde çalışacak şekilde tasarlandı - birçok bağlamda intve bir işaretçinin birbirinin yerine kullanılabileceği. Bir rutin bir gösterici bekliyor ve bir tamsayı 57'den geçerse, rutin 57 sayısı ile aynı bit modeline sahip adresi kullanır. Bu belirli makinelerde, bir boş göstericiyi belirtmek için bit örüntüsü 0'dı, dolayısıyla bir int 0 geçmek boş gösterici geçecekti.
2013

O zamandan beri, C, farklı sayılar ve işaretçilerle çok çeşitli başka makineler için programlar yazmak için kullanılabilecek şekilde gelişti. Sıfır olmayan sayısal sabitler nadiren işaretçi olarak kullanılırken, sabit sayısal sıfırlar, boş işaretçileri temsil etmek için yaygın olarak kullanılmıştır. Böyle bir kullanıma izin vermemek var olan kodu bozmuş olacaktı, bu nedenle derleyicilerin sayısal bir sıfırı uygulamanın bir boş göstericiyi temsil etmek için kullandığı her şeye çevirmesi bekleniyordu.
2013

-4

( Lütfen Yazıyı Okumadan Önce Bu Paragrafı Okuyun. Bu yazıyı okumakla ilgilenen herkesten dikkatlice okumasını istiyorum ve tabii ki tamamen anlayana kadar olumsuz oy vermeyin, teşekkürler.)

Bu artık topluluk wiki'dir, çünkü birisi kavramlardan herhangi birine katılmazsa, lütfen neyin yanlış olduğunu ve nedenini açık ve ayrıntılı bir şekilde açıklayarak değiştirin ve mümkünse lütfen kaynakları belirtin veya çoğaltılabilecek kanıtlar sağlayın.

Cevap

NULL == 0'ın altında yatan faktörler olabilecek diğer birkaç neden burada.

  1. Sıfırın yanlış olduğu gerçeği, bu nedenle kişi if(!my_ptr)yerine doğrudan yapabilirif(my_ptr==NULL) .
  2. Başlatılmamış küresel tamsayıların varsayılan olarak tümü sıfır olarak başlatılması ve bu nedenle tüm sıfırların bir göstericisi başlatılmamış olarak kabul edilir.

Burada Diğer Cevaplar Üzerine Bir Kelime Söylemek İstiyorum

Sözdizimsel Şekerden Değil

Sözdizimsel şeker nedeniyle NULL'un sıfır olduğunu söylemek çok mantıklı değil, öyleyse neden uzunluğunu tutmak için dizinin 0 indisini kullanmıyoruz?

Aslında C, iç uygulamaya en çok benzeyen dildir, C'nin sırf sözdizimsel şeker yüzünden sıfırı seçtiğini söylemek mantıklı olur mu? Sıfırı NULL ile eşleştirmek yerine boş bir anahtar kelime (diğer birçok dilin yaptığı gibi) sağlamayı tercih ederler!

Bugün itibariyle sadece sözdizimsel şeker olsa da, daha fazla göstereceğim gibi C dili geliştiricilerinin asıl niyetinin sözdizimsel şeker için olmadığı açıktır.

1) Şartname

Yine de, C spesifikasyonunun sıfır gösterici olarak 0 sabitinden konuştuğu (bölüm 6.3.2.3) ve ayrıca uygulama tanımlanacak şekilde NULL tanımladığı doğru olsa da (C11 spesifikasyonunda bölüm 7.19 ve C99 spesifikasyonunda 7.17), gerçek şu ki, C'nin mucitleri tarafından yazılan "C Programlama Dili" kitabında aşağıdaki bölüm 5.4'te belirtilmiştir:

C, sıfırın hiçbir zaman veriler için geçerli bir adres olmadığını garanti eder, bu nedenle sıfır dönüş değeri, anormal bir olayı işaret etmek için kullanılabilir, bu durumda boşluk yok.

İşaretçi ve tamsayılar birbirinin yerine geçemez, Sıfır tek istisnadır: sabit sıfır bir işaretçiye atanabilir ve bir işaretçi sabit sıfır ile karşılaştırılabilir. Sembolik sabit NULL, bunun bir işaretçi için özel bir değer olduğunu daha açık bir şekilde belirtmek için bir anımsatıcı olarak genellikle sıfır yerine kullanılır. NULL, içinde tanımlanmıştır. Bundan sonra NULL kullanacağız.

Görüldüğü gibi ("sıfır adres" sözcüklerinden) en azından C'nin yazarlarının asıl niyetleri sıfır adresindeydi ve sabit sıfır değil, ayrıca bu alıntıdan belirtimin neden sabit sıfır, büyük olasılıkla sıfır olarak değerlendirilen bir ifadeyi hariç tutmak değil, bunun yerine çevrim yapmadan bir işaretçi bağlamında kullanılmasına izin verilen tek tamsayı sabiti sıfır tamsayı sabitini dahil etmektir.

2) Özet

Spesifikasyon, bir sıfır adresinin sıfır sabitinden farklı olarak ele alınabileceğini açıkça söylemese de, bunun olmadığını ve boş işaretçi sabiti ile uğraşırken bunun uygulama olarak tanımlandığını iddia etmez. NULL tarafından yapar yerine sıfır sabit ve sıfır adresi arasında bir fark olabileceğini sıfır, Şekil olduğu İstem tanımlandığı sabit.

(Bununla birlikte, eğer durum buysa, NULL uygulamasının neden tanımlandığını merak ediyorum, çünkü böyle bir durumda NULL aynı zamanda sabit sıfır olabilir, çünkü derleyici yine de tüm sıfır sabitlerini NULL tanımlı gerçek uygulamaya dönüştürmelidir?)

Ancak bunu gerçek eylemde görmüyorum ve genel platformlarda adres sıfır ve sabit sıfır aynı şekilde ele alınır ve aynı hata mesajını verir.

Dahası, gerçek şu ki, bugünün işletim sistemleri, C'nin NULL göstericisi nedeniyle sıfır adrese erişimi önlemek için aslında ilk sayfanın tamamını (0x0000 ila 0xFFFF aralığı) ayırmaktadır (bkz. Http://en.wikipedia.org/wiki/ Zero_page ve "Windows Via C / C ++, Jeffrey Richter ve Christophe Nasarre (Microsoft Press tarafından yayınlandı)").

Bu nedenle, gerçekten eylemde görüldüğünü iddia eden herkesten platformu, derleyiciyi ve gerçekte yaptığı tam kodu belirtmesini rica ediyorum (spesifikasyondaki belirsiz tanım nedeniyle [gösterdiğim gibi] herhangi bir derleyici ve platform ne isterse yapmakta özgürdür).

Bununla birlikte, görünüşe göre C'nin yazarlarının bunu akıllarında tutmadıkları ve "sıfır adres" den söz ettikleri ve "C'nin hiçbir zaman geçerli bir adres olmadığını garanti ettiği" ve "NULL yalnızca bir anımsatıcı ", orijinal amacının" sözdizimsel şeker "için olmadığını açıkça gösteriyor.

İşletim Sistemi Nedeniyle Değil

Ayrıca, birkaç nedenden dolayı işletim sisteminin sıfır adresine erişimi reddettiğini iddia etmek:

1) C yazıldığında, bu wikipage http://en.wikipedia.org/wiki/Zero_page'de görebileceğiniz gibi, böyle bir kısıtlama yoktu .

2) Gerçek şu ki, C derleyicileri sıfır bellek adresine erişmiştir.

Bu, BellLabs'ın aşağıdaki makalesinde ( http://www.cs.bell-labs.com/who/dmr/primevalC.html )

İki derleyici, bununla nasıl başa çıktıkları konusunda ayrıntılarda farklılık gösterir. Daha öncekinde, başlangıç ​​bir işlevi adlandırarak bulunur; daha sonra, başlangıç ​​0 olarak alınır. Bu, ilk derleyicinin biz bellek eşlemeli bir makineye sahip olmadan önce yazıldığını gösterir, bu nedenle programın başlangıcı 0 konumunda değildi, oysa ikincinin zamanında, haritalama sağlayan bir PDP-11'imiz vardı.

(Aslında bugün itibariyle (yukarıda wikipedia ve microsoft press'den alıntı yaptığım gibi), sıfır adrese erişimi kısıtlamanın nedeni C'nin NULL işaretçileridir! Yani sonunda tam tersi olduğu ortaya çıktı!)

3) C'nin aynı zamanda işletim sistemleri ve hatta C derleyicileri yazmak için kullanıldığını unutmayın!

Aslında C, onunla birlikte UNIX işletim sistemini yazmak amacıyla geliştirildi ve bu nedenle kendilerini sıfır adresinden kısıtlamaları için hiçbir neden yok gibi görünüyor.

(Donanım) Bilgisayarların Sıfır Adresine Nasıl (Fiziksel Olarak) Erişebildiğine İlişkin Açıklama

Burada açıklamak istediğim bir nokta daha var, sıfır adresini referans almak nasıl mümkün olabilir?

Bir saniyeliğine düşünün, adresler işlemci tarafından alınır ve ardından bellek veriyolunda voltajlar olarak gönderilir, bu daha sonra bellek sistemi tarafından gerçek adrese ulaşmak için kullanılır ve yine de sıfır adresi voltaj olmadığı anlamına gelmez. , bellek sisteminin fiziksel donanımı sıfır adresine nasıl erişiyor?

Cevap, bu adresin varsayılan olduğu ve başka bir deyişle, adres sıfıra her zaman bellek sistemi tarafından bellek veriyolu tamamen kapalı olduğunda ve bu nedenle gerçek bir adres belirtmeden herhangi bir okuma veya yazma talebi (ki sıfır adresli durum) otomatik olarak sıfır adresine erişiyor.


1
Size olumsuz oy vermedim, ancak yayınınızda birkaç gerçek yanlışlık var, ör. ofset 0'daki fiziksel belleğe erişmek imkansızdır (tüm anahtarların kapalı olması nedeniyle mi? gerçekten?), 0 ve sabiti 0 birbirinin yerine kullanılabilir (bunlar olmayabilir) ve diğerleri.
Hasturkun

0 ve sabit sıfır ile ilgili olarak, orijinal kitabın söylediği budur ve bu gerçek testin gösterdiği şey, ikisi arasında gerçek bir fark buldunuz mu? Varsa, hangi derleyici ve platform? Pek çok cevap benim bulmadığım bir fark olduğunu öne sürse de, farklılık gösterecek bir referansları yok. Aslında en.wikipedia.org/wiki/Zero_page'e göre Ve ayrıca "Windows Via C / C ++, Jeffrey Richter ve Christophe Nasarre (Microsoft Press tarafından yayınlandı)" ilk sayfanın tamamı! sadece
boşluğu

Elbette adresin bit şablonu, okunmakta olanı seçmek için kullanılır. Genelde durum böyledir. her neyse, seninle tartışmak istemiyorum, sadece neden olumsuz oy verildiğine işaret ediyordum.
Hasturkun

İddialarınıza katılmıyorum. Bu tartışmaya devam etmekle de ilgilenmiyorum.
Hasturkun

6
Donanım iddiası saçma. Sıfır adresini okumak için,! Chip Select düşük,! RAS yüksek,! CAS düşük,! WE yüksek ve tüm adres satırları düşük sürün. Otobüs kapalıyken! CS yüksektir.
MSalters
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.