Ne Kışkırtıcı Bir Soru!
Bu konudaki yanıtların ve yorumların el yazısı taraması bile , görünüşte basit ve doğrudan sorgunun ne kadar duygusal olduğunu ortaya çıkaracaktır .
Şaşırtıcı olmamalı.
Tartışmasız, yanlış anlamalar etrafında kavram ve kullanım ait işaretçiler bir baskın temsil nedenini ciddi başarısızlıkları genelde programlama içinde.
Bu gerçeğin tanınması, özellikle işaretçilerin tamamen ortaya koyduğu zorluklardan kaçınmak ve tercihen bunlardan kaçınmak için tasarlanmış dillerin çoğunda açıkça görülmektedir . C ++ ve C, Java ve ilişkilerinin diğer türevlerini, Python ve diğer senaryoları düşünün - sadece daha belirgin ve yaygın olanlar olarak ve sorunla başa çıkma şiddeti bakımından az çok sıralı.
Altında yatan ilkelerin daha derin bir anlayış geliştirmek, bu nedenle olmalıdır ilgili hiç o Aspire serisi her bireyin mükemmellik özellikle sistemler düzeyinde - programlama içinde .
Bunun öğretmeninizin göstermek istediği şey olduğunu hayal ediyorum.
Ve C'nin doğası onu bu keşif için uygun bir araç yapar. Yürütme ortamının daha derin soyutlanmasına dayanan dillerden - belki de daha kolay anlaşılabilir olsa da - derlemeden daha az açıktır.
Programlayıcının niyetinin makinelerin anlayabileceği talimatlara deterministik olarak çevrilmesini kolaylaştırmak için tasarlanan C, sistem düzeyinde bir dildir. Üst düzey olarak sınıflandırılırken, gerçekten bir 'orta' kategoriye girer; fakat böyle bir şey olmadığından, 'sistem' ataması yeterli olmak zorundadır.
Bu özellik, aygıt sürücüleri , işletim sistemi kodu ve katıştırılmış uygulamalar için tercih edilen bir dil haline getirilmesinden büyük ölçüde sorumludur . Ayrıca, optimum verimliliğin en önemli olduğu uygulamalarda haklı olarak tercih edilen bir alternatif ; burada bu, hayatta kalma ve yok olma arasındaki fark anlamına gelir ve bu nedenle bir lüksün aksine bir zorunluluktur . Bu gibi durumlarda, taşınabilirliğin cazip rahatlığı tüm cazibesini kaybeder ve en az ortak paydanın parlaklık eksikliği performansını seçmek düşünülemez derecede zararlı bir seçenek haline gelir .
Ne C yapar - ve bunun bazı türevleri - çok özel, tam o verir kullanıcılarının tam kontrolü - onlar ne arzu olduğunda - olmadan empoze ilgili sorumlulukları onlar değil zaman onların üzerine. Bununla birlikte, hiç olmamasından daha sunuyor izolasyonları ince gelen makinenin doğru kullanımı beklerken niçin talep titiz anlama kavramının işaretçiler .
Özünde, sorunuzun cevabı, şüphelerinizin doğrulanmasında, oldukça basit ve tatmin edici derecede tatlıdır. Bununla birlikte, bu ifadedeki her konsepte gerekli önemi vermek şartıyla :
- İşaretçileri, inceleyerek karşılaştırma ve manipüle eylemleri her zaman ve her vardır mutlaka sonucu elde edilen sonuçlar alan değerler geçerliliğine bağlıdır iken, geçerli ve böylece gerek yok ol.
Eski hem kaçınılmaz güvenli ve potansiyel olarak uygun ikincisi kutu sadece hiç olmak ederken, uygun o edildiğinde kurulmuş olarak güvenli . Şaşırtıcı bir şekilde - bazıları için - ikincisinin geçerliliğini belirlemek öncekine bağlıdır ve talep eder.
Tabii ki, karışıklığın bir kısmı, bir işaretçi ilkesi içinde doğal olarak mevcut olan özyinelemenin etkisinden ve içeriği adresten ayırmada karşılaşılan zorluklardan kaynaklanır.
Oldukça doğru bir şekilde tahmin ettiniz ,
Herhangi bir işaretçinin, tek tek nerede işaret ettiklerine bakılmaksızın, diğer işaretçilerle karşılaştırılabileceğini düşünüyorum. Dahası, sanırım iki işaretçi arasındaki işaretçi aritmetiği iyi, tek tek nerede işaret ettikleri önemli değil, çünkü aritmetik sadece işaretçiler deposunun bellek adreslerini kullanıyor.
Ve bazı katkıda bulunanlar doğruladı: işaretçiler sadece rakamlardır. Bazen karmaşık sayılara daha yakın , ama yine de sayılardan daha fazla olmayan bir şey.
Bu çekişmenin burada alındığı eğlenceli acrimony, insan doğası hakkında programlamadan daha fazlasını ortaya koyuyor, ancak not ve ayrıntıya değer. Belki daha sonra yapacağız ...
Bir yorum ipucu vermeye başladığında; tüm bu karışıklık ve şaşkınlık , güvenli olandan geçerli olanı ayırt etme ihtiyacından kaynaklanır , ancak bu aşırı basitleştirmedir. Ayrıca neyin işlevsel ve neyin güvenilir olduğunu , neyin pratik olduğunu ve neyin uygun olabileceğini ve ayrıca daha da ayırt etmeliyiz : belirli bir durumda neyin uygun olduğunu daha genel anlamda neyin uygun olabileceğinden ayırmalıyız . Lafı olmaz; arasındaki fark uygun ve uygunluk .
Bu amaçla doğru, öncelikle gerek takdir kesin bir şey işaretçi olduğunu .
- Konsept üzerinde sağlam bir kavrayış gösterdiniz ve diğerleri gibi bu illüstrasyonları da patlayıcı bir şekilde basit bulabilirler, ancak burada belirgin olan karışıklık seviyesi, açıklamada bu kadar basitlik gerektirir .
Birçoğunun işaret ettiği gibi: işaretçi terimi , sadece bir dizin olan şey için özel bir isimdir ve bu nedenle diğer herhangi bir sayıdan başka bir şey değildir .
Bu , tüm çağdaş anaakım bilgisayarların yalnızca sayılarla ve sayılarla çalışması gereken ikili makineler olduğu gerçeği göz önüne alındığında zaten açıkça görülmelidir . Kuantum bilgisayar olabilir bunu değiştirmek, ama bu pek olası olduğunu ve yaş gelmedi.
Teknik olarak, belirttiğiniz gibi, işaretçiler daha doğru adreslerdir ; doğal olarak, onları bir sokaktaki evlerin veya arsaların 'adresleri' ile ilişkilendirmenin ödüllendirici benzetmesini doğal olarak tanıtan açık bir içgörü.
Bir de düz bellek modeli: Tüm sistem belleği tek bir doğrusal sırayla düzenlenir: aynı yolda şehir yalan tüm evler ve her ev benzersiz, tek başına kendi numarası ile tanımlanır. Nefis basit.
In segmentli düzenleri: numaralı yolların bir hiyerarşik örgüt kompozit adresi gereklidir böylece numaralı evlerin yukarıda tanıtıldı.
- Bazı uygulamalar hala daha kıvrımlıdır ve farklı 'yolların' bütünlüğünün bitişik bir sıralamayı toplamasına gerek yoktur , ancak bunların hiçbiri altta yatan şeyle ilgili hiçbir şeyi değiştirmez.
- Mutlaka bu tür hiyerarşik bağlantıların hepsini düz bir organizasyona ayırabiliriz. Organizasyon ne kadar karmaşıksa, bunu yapmak için o kadar fazla çember atlamak zorunda kalacağız, ancak bu mümkün olmalıdır . Aslında bu, x86'daki 'gerçek mod' için de geçerlidir.
- Aksi takdirde, konumlara bağlantıların eşleştirilmesi iki yönlü olmazdı , çünkü güvenilir yürütme - sistem düzeyinde - olması GEREKİR .
- Birden çok adresi gerekir değil tekil hafıza yerlere haritasına ve
- tekil adresler asla birden fazla bellek konumuna eşlenmemelidir.
Bizi , bilmeceyi böylesine büyüleyici derecede karmaşık bir arapsaçı haline getiren daha fazla büküme getirin . Yukarıda, işaretçileri önermek uygun olduğunu vardır basitlik ve açıklık amacıyla, adresleri. Tabii ki, bu doğru değil . Bir işaretçi olduğu olmayan bir adres; bir işaretçi olduğu bir referans bir adrese bu içerir bir adres . Zarf sporları gibi ev için bir referans. Bunu düşünmek, kavramda yer alan özyineleme önerisiyle ne anlama geldiğini anlamanıza neden olabilir. Yine; çok fazla sözümüz var ve adreslere yapılan referansların adresleri hakkında konuşuyoruzve böyle, yakında beynin çoğunu geçersiz bir op kodu istisnasında durdurur . Ve çoğunlukla, amaç bağlamdan kolayca elde edilir, bu yüzden sokağa dönelim.
Bu hayali kentimizdeki posta işçileri, 'gerçek' dünyada bulduklarımıza çok benziyorlar. Geçersiz bir adres hakkında konuştuğunuzda veya sorguladığınızda hiç kimsenin felç geçirmesi olası değildir , ancak bu bilgiler üzerinde hareket etmelerini istediğinizde sonuncusu duracaktır .
Tekil caddemizde sadece 20 ev olduğunu varsayalım. Ayrıca, yanlış yönlendirilmiş veya disleksik bir ruhun çok önemli bir mektubu 71 numaraya yönlendirdiğini varsayalım. Şimdi, taşıyıcı Frank'e böyle bir adres olup olmadığını sorabiliriz ve basitçe ve sakin bir şekilde rapor verir: hayır . Hatta onu o takdirde bu konum yalan olur ne kadar sokakta dışında tahmin etmek bekleyebilirsiniz yaptım kabaca 2.5 kat daha fazla ucundan daha: mevcuttur. Bunların hiçbiri ona öfkeye neden olmaz. Ancak, biz ona sormak olsaydı teslim bu mektubu, ya da pick up o yerden bir öğe onun hakkında oldukça dürüst olması muhtemeldir hoşnutsuzluk ve ret uymak.
İşaretçiler yalnızca adreslerdir ve adresler yalnızca sayılardır.
Aşağıdakilerin çıktısını doğrulayın:
void foo( void *p ) {
printf(“%p\t%zu\t%d\n”, p, (size_t)p, p == (size_t)p);
}
Geçerli veya değil, istediğiniz kadar işaretçi olarak çağırın. Lütfen do sizin platformda başarısız veya eğer bulgularını yayınlamak (çağdaş) derleyici şikayet ediyor.
İşaretçileri çünkü Şimdi, olan sadece sayılar, bunları karşılaştırmak için kaçınılmaz geçerlidir. Bir anlamda öğretmeninizin gösterdiği şey tam olarak budur. Aşağıdaki ifadelerin tümü mükemmel bir şekilde geçerlidir - ve düzgün! - C ve derlendiğinde , hiçbir işaretçinin başlatılmasına gerek olmadığından ve içerdikleri değerler tanımlanmamış olsa bile, sorun yaşamadan çalışacaktır :
- Biz sadece hesaplıyoruz
result
açıkça uğruna netlik ve baskı o kadar zorlamak aksi gereksiz, ölü kod ne olacağını hesaplamak için derleyici.
void foo( size_t *a, size_t *b ) {
size_t result;
result = (size_t)a;
printf(“%zu\n”, result);
result = a == b;
printf(“%zu\n”, result);
result = a < b;
printf(“%zu\n”, result);
result = a - b;
printf(“%zu\n”, result);
}
Elbette , test noktasında a veya b tanımsız olduğunda (okuma: doğru şekilde başlatılmamışsa ) program yanlış biçimlendirilmiştir , ancak bu tartışmamızın bu kısmı ile tamamen alakasızdır . Bu parçacıklar, aşağıdaki ifadeler gibi, 'standart' ile - herhangi bir işaretçinin IN- geçersizliğine bakılmaksızın, kusursuz bir şekilde derlenmesi ve çalışması için garanti edilir .
Sorunlar yalnızca geçersiz bir işaretçi kaydı silindiğinde ortaya çıkar . Frank'ten geçersiz, var olmayan adresi almasını veya teslim etmesini istediğimizde.
Herhangi bir keyfi işaretçi verildiğinde:
int *p;
Bu ifadenin derlenmesi ve çalıştırılması gerekir:
printf(“%p”, p);
... olması gerektiği gibi:
size_t foo( int *p ) { return (size_t)p; }
... ikisini takip tezat, hala kolaylıkla derlemek, ancak başarısız yürütme sürece işaretçi olan geçerli - hangi biz burada sadece bu anlama mevcut uygulama erişim izni edildiği bir adresi başvuruyor :
printf(“%p”, *p);
size_t foo( int *p ) { return *p; }
Değişim ne kadar incedir? İşaretçi değeri arasındaki fark olarak fark yatıyor - olan adres ve içindekiler değeri: evi bu numaradan. İşaretçi kayıttan kaldırılana kadar hiçbir sorun ortaya çıkmaz ; bağlandığı adrese erişilmeye çalışılana kadar. Paketi yolun ötesine taşımaya veya almaya çalışırken ...
Ek olarak, aynı prensip mutlaka yukarıda belirtilen içeren daha karmaşık örnekler için de geçerlidir ihtiyacı için kurmak gerekli geçerliliğini:
int* validate( int *p, int *head, int *tail ) {
return p >= head && p <= tail ? p : NULL;
}
İlişkisel karşılaştırma ve aritmetik, denkliği test etmek için aynı yarar sağlar ve prensipte eşdeğer olarak geçerlidir. Ancak , bu tür hesaplama sonuçları ne delalet ve hassas sorun, dahil alıntılarla tarafından ele - farklı tamamen meselesidir.
C'de bir dizi bitişik bir arabellek, kesintisiz doğrusal bellek dizileri dizisidir. Böyle bir tekil seri içindeki konumlara referans veren işaretçiler için uygulanan karşılaştırma ve aritmetik , doğal olarak ve hem birbirleriyle hem de bu 'dizi'ye (temel olarak tanımlanır) göre açıkça anlamlıdır. Aynısı malloc
, veya yoluyla tahsis edilen her blok için de geçerlidir sbrk
. Çünkü bu ilişkilerin örtük , derleyici aralarında geçerli ilişkiler kurmak yapabiliyor ve bu nedenle olabilir emin hesaplamalar beklenen yanıtları sunmasını.
Farklı bloklara veya dizilere gönderme yapan işaretçiler üzerinde benzer jimnastik yapmak , böyle doğal ve belirgin bir fayda sunmaz . Ne kadar fazla olursa olsun, bir anda hangi ilişki olursa olsun, bunun değişme olasılığı yüksek, tersine çevrilebilir bir yeniden tahsis ile geçersiz kılınabilir. Bu gibi durumlarda derleyici, önceki durumda sahip olduğu güveni tesis etmek için gerekli bilgileri elde edemez.
Sen , ancak, programcı olarak, olabilecek bu tür bilgiye sahip! Ve bazı durumlarda bundan faydalanmakla yükümlüdür.
Orada ARE hangi şartlarda, bu nedenle, ÇİFT BU tamamen GEÇERLİ ve mükemmel ÖZELLİK.
Aslında, yani tam olarak neyi malloc
mimarileri büyük çoğunluğu üzerinde - kendisi zaman geri bloklarını birleştirme denemek için geldiğinde içten ilgisi var. Aynısı, işletim sistemi ayırıcısı için de geçerlidir sbrk
; arkadaki gibi ; eğer daha açıkçası , sık , üzerinde daha fazla birbirinden farklı varlıklar, daha eleştirel - ve de bu platformlarda alakalı malloc
olmayabilir. Ve bunların kaç edilir değil C ile yazılmış?
Bir projenin geçerliliği, güvenliği ve başarısı, kaçınılmaz olarak, üzerine inşa edildiği ve uygulandığı içgörü düzeyinin sonucudur.
Sunduğunuz alıntılarda, Kernighan ve Ritchie birbiriyle yakından ilgili, ancak yine de ayrı bir konuyu ele alıyor. Onlar edilir tanımlayan sınırlamaları içinde dili , ve en azından potansiyel olarak hatalı yapıları tespit ederek sizi korumak için derleyici yetenekleri yararlanmak nasıl açıklayan. Programlama görevinizde size yardımcı olmak için mekanizmanın kullanabileceği - tasarlandığı - uzunlukları açıklarlar. Derleyici kul olunuz, sen olan usta. Ancak bilge bir usta, çeşitli hizmetkarlarının yeteneklerine yakından aşina olan bir ustadır.
Bu bağlamda, tanımlanmamış davranış potansiyel tehlikeyi ve zarar olasılığını göstermeye hizmet eder; bildiğimiz gibi yakın, geri dönüşü olmayan bir kıyamet veya dünyanın sonu anlamına gelmemelidir. Bu sadece anlamına gelir biz 'derleyici anlam' - - bu şey ne olabilir hakkında herhangi bir kestirimde bulunmak veya temsil etmek mümkün değildir ve bu nedenle biz maddenin ellerimizi yıkamak için seçin. Bu tesisin kullanımından veya yanlış kullanımından kaynaklanabilecek herhangi bir talihsizlikten sorumlu tutulamayacağız .
Aslında şunu söylüyor: 'Bu noktanın ötesinde kovboy : tek başınasınız ...'
Profesörünüz size daha ince nüansları göstermeye çalışıyor .
Örneklerini hazırlarken ne kadar özen gösterdiklerine dikkat edin ; ve nasıl kırılgan o hala olduğunu. Adresini alarak a
içinde,
p[0].p0 = &a;
derleyici, değişkenin bir kayıt defterine yerleştirilmesinden ziyade, gerçek depolama alanının tahsis edilmesine zorlanır. Ancak bu otomatik bir değişkendir, programcının atanmış olduğu yer üzerinde herhangi bir kontrolü yoktur ve bu nedenle onu takip edecek şey hakkında geçerli bir tahmin yapamaz. Hangi neden olmalıdır beklendiği gibi çalışmaya kodu için sıfıra eşit ayarlanmalıdır.a
Sadece bu satırı değiştirmek:
char a = 0;
buna:
char a = 1; // or ANY other value than 0
programın davranışının tanımlanmamasına neden olur . En azından, ilk cevap şimdi 1 olacak; ama sorun çok daha sinsi.
Şimdi kod felakete davet ediyor.
Hala mükemmel derecede geçerli ve hatta standarda uygun olsa da , şimdi yanlış biçimlendirilmiştir ve derlenmesine rağmen, çeşitli zeminlerde uygulamada başarısız olabilir. Şimdilik orada birden problemler - hiçbiri hangi derleyici olduğunu edebilmek için tanımak.
strcpy
adresinde başlayacak a
ve bayttan sonra baytı tüketmek ve boş bir değerle karşılaşıncaya kadar aktarmak için bunun ötesine geçecektir .
p1
İşaretçi tam bir blok başlatıldı 10 bayt.
Eğer a
bir blok sonunda yerleştirilecek olur ve süreç, hemen ertesi okuma ne şu erişimi yok - p0 [1] - Bir segfault ortaya çıkartacaktır. Bu senaryo x86 mimarisinde olası değildir , ancak mümkündür.
Adresi dışındaki alan ise a
ise erişilebilir, hiçbir okuma hatası ortaya çıkar, ama yine de programın talihsizliğinden kaydedilmez.
Eğer sıfır bayt olur adresinde başlayan on içinde gerçekleşmesi a
, bu olabilir o zaman için hala hayatta strcpy
durur ve en azından bir yazma ihlali acı olmaz.
Eğer yanlış okumak için hatalı değilse , ancak bu 10 aralıkta sıfır bayt oluşmazsa, strcpy
devam edecek ve tahsis edilen bloğun ötesine yazmaya çalışacaktır malloc
.
Eğer bu alan sürece ait değilse, segfault derhal tetiklenmelidir.
Hala daha felaket - ve ince --- durum aşağıdaki blok ortaya çıkar edilir , daha sonra hata için işleme ait olamaz algılanabilir, sinyal kaldırdı ve bu yüzden olabilir hala 'iş' için 'görünür' , aslında edilecek ise üzerine yazarak (bazı işletim ortamlarında) diğer verileri, sizin allocator yönetim yapılarını, hatta kod.
Bu yüzden işaretçi ilgili hatalar yüzden olabilir sert etmek izlemek . Bu satırların, bir başkasının yazdığı ve sizi araştırmaya yönlendirilen, karmaşık olarak ilgili binlerce satırın derinliklerine gömüldüğünü düşünün.
Yine , program olmalı o kalır için hala derlemek tamamen geçerli ve standart uyumluluğunu C
Bu tür hatalar, hiçbir standart ve hiçbir derleyici istenmeyenlere karşı koruyamaz. Sanırım tam olarak size öğretmek istedikleri şey bu.
Paranoid insanlar sürekli etmeye değiştirmek doğayı bu sorunlu olasılıklar imha ve böylece bizi kendimizden kurtarmak için C; ama bu çok saçma . Bu, gücü takip etmeyi ve makinenin daha doğrudan ve kapsamlı kontrolünün bize sunduğu özgürlüğü elde ettiğimizde kabul etmek zorunda olduğumuz sorumluluktur . Performansta mükemmelliği geliştirenler ve takipçiler asla daha az bir şey kabul etmezler.
Taşınabilirlik ve genelliği , temsil ettiği bir temelde ayrı göz ve bir bütün olduğunu standart adresine arar:
Bu belge, programlama dilinde C ifade edilen programların formunu belirtir ve yorumlanmasını sağlar. Amacı , C dil programlarının çeşitli bilgisayar sistemlerinde taşınabilirliğini , güvenilirliğini, sürdürülebilirliğini ve verimli yürütülmesini teşvik etmektir .
Hangisi tutmak için mükemmel uygun olmasının nedeni ise farklı gelen tanım ve teknik şartname dilin kendisinin. Birçok aksine inanmak gibi genelliği olan zıt için istisnai ve de örnek .
Sonuç olarak:
- İşaretçileri incelemek ve manipüle etmek her zaman geçerli ve genellikle verimlidir . Sonuçların yorumlanması anlamlı olabilir veya olmayabilir, ancak işaretçi kayıttan kaldırılana kadar felaket asla davet edilmez ; bağlantı verilen adrese erişilmeye çalışılana kadar .
Bu doğru olmasaydı, bildiğimiz gibi programlama - ve onu sevmek - mümkün olmazdı.
C
ne ile güvenli bölgesiC
. Bununla birlikte, iki türün aynı türle karşılaştırılması her zaman yapılabilir (örneğin eşitlik kontrolü), işaretçi aritmetiği kullanılarak ve karşılaştırılır>
ve<
yalnızca belirli bir dizi (veya bellek bloğu) içinde kullanıldığında güvenlidir .