İşaretçiler ne zaman ve neden riskli olarak görülmeye başladı?


18

Programlama dillerinde işaretçilerin kullanımı hakkında düşünmede kademeli bir değişim olduğu anlaşılmaktadır, öyle ki işaretçiler riskli olarak kabul edilir (açıkça "kötü" veya benzer bir artış olmasa da).

Düşünmedeki bu değişim için tarihsel gelişmeler nelerdi? Özel, seminal olaylar, araştırmalar veya başka gelişmeler oldu mu?

Örneğin, C'den C ++ 'a Java'ya geçişe yüzeysel bir bakış, işaretçileri takviye etmek ve daha sonra tamamen referanslarla değiştirmek için bir eğilim göstermektedir. Bununla birlikte, gerçek olay zinciri muhtemelen bundan çok daha ince ve karmaşıktı ve neredeyse ardışık değildi. Bunu ana akım diller haline getiren özellikler başka bir yerde, belki de çok daha önce ortaya çıkmış olabilir.

Not: Göstericilerin gerçek referansları ve referanslara karşı başka bir şey sormuyorum. Odaklandığım nokta bu belirgin değişimin rasyonelleri.


1
Bunun nedeni Liberal Sanatlar eğitiminin azalmasıydı. İnsanlar artık tüm CPU'larda bulunan bilgi işlem teknolojisinin en temel fikirlerinden biri olan Dolaylı Referans'ı anlayamadılar.

10
İşaretçiler olan riskli. Sizce neden düşünme konusunda bir değişiklik oldu? Herhangi bir performans cezası olmasa da, işaretçiler olmadan yazılım yazmaya izin veren dil özellikleri ve donanımında iyileştirmeler yapılmıştır.
Monica'ya zarar vermeyi durdur

4
@DaveInCaz Bildiğim kadarıyla belirli bir gelişme işaretçilerin icadıdır.
Monica

5
@nocomprende: az önce yazdığın gerçekler ya da kanıt değil, sadece fikir. 1970'de çok daha az programcı vardı, bugün "dolaylı referans" ta nüfusun daha iyi ya da kötü olduğuna dair bir kanıtınız yok.
whatsisname

3
İşaretçiler ilk günden itibaren her zaman riskli kabul edilir. Bunları derlemeden daha üst düzey dillere taşımak bir uzlaşma oldu.
Frank Hileman

Yanıtlar:


21

Gerekçe işaretçiler için alternatiflerin geliştirilmesiydi.

Kaputun altında, herhangi bir işaretçi / referans / vb, bir bellek adresi (aka işaretçi) içeren bir tamsayı olarak uygulanmaktadır. Ne zaman C çıktı bu işlev işaretçileri olarak ortaya çıkmıştır. Bu, temel donanımın belleği adreslemek için yapabileceği her şeyin işaretçilerle yapılabileceği anlamına geliyordu.

Bu her zaman "tehlikelidir", ancak tehlike görecelidir. 1000 satırlık bir program hazırlarken veya IBM kalitesinde yazılım kalite prosedürleri uyguladığınızda, bu tehlike kolayca giderilebilir. Ancak, tüm yazılımlar bu şekilde geliştirilmiyordu. Bu nedenle, daha basit yapılar için bir arzu ortaya çıktı.

Bunu düşünürseniz, bir int&ve int* constgerçekten aynı güvenlik seviyesine sahiptir, ancak birinin diğerinden daha güzel bir sözdizimi vardır. int&ayrıca daha verimli olabilir çünkü bir kayıtta saklanan bir int'e işaret edebilir (anakronizm: bu geçmişte doğruydu, ancak modern derleyiciler optimizasyonda o kadar iyi ki, bir kayıttaki bir tamsayı için bir işaretçi olabildiğince gerçek adres gerektirecek özelliklerden hiçbirini asla kullanmazsınız, örneğin ++)

Java'ya geçtikçe , bazı güvenlik garantileri sağlayan dillere geçiyoruz. C ve C ++ hiçbiri sağlamadı. Java, yalnızca yasal işlemlerin yürütülmesini garanti eder. Bunu yapmak için, java işaretçilerle tamamen uzaklaştı. Buldukları şey, gerçek kodda yapılan işaretçi / referans işlemlerinin büyük çoğunluğunun, referansların fazlasıyla yeterli olduğu şeylerdi. Sadece bir avuç durumda (bir dizi üzerinden hızlı yineleme gibi) gerçekten gerekli işaretçiler vardı. Bu durumlarda, java bunları kullanmaktan kaçınmak için bir çalışma zamanı isabeti alır.

Hareket tekdüze değildi. C # çok sınırlı bir biçimde olsa da, işaretçileri yeniden tanıttı . " Güvensiz " olarak işaretlenirler , yani güvenilir olmayan kod tarafından kullanılamazlar. Ayrıca neye işaret edebilecekleri ve nelere işaret edemeyecekleri konusunda açık kurallara sahiptirler (örneğin, bir işaretçiyi bir dizinin sonunu geçecek şekilde artırmak geçersizdir ). Bununla birlikte, işaretçilerin yüksek performansının gerekli olduğu birkaç durum olduğunu keşfettiler, bu yüzden onları tekrar yerleştirdiler.

Ayrıca ilgi çekici olan fonksiyonel diller de ilgi çekiciydi, ancak bu çok farklı bir tartışma.


3
Java'nın işaretçisi olmadığını söylemenin doğru olduğundan emin değilim. İşaretçi nedir ve değil hakkında uzun bir tartışma içine girmek istemiyorum ama JLS "bir referans değeri bir işaretçi" diyor. Yalnızca doğrudan erişim veya işaretçiler üzerinde değişiklik yapılmasına izin verilmez. Bu sadece güvenlik için değil, insanları bir nesnenin her an nerede olduğunu takip etmek işinden uzak tutmak GC için faydalıdır.
JimmyJames

6
@JimmyJames Doğru. Bu cevabın amaçları için, işaretçi ve işaretçi olmayan arasındaki bölme çizgisi, tipik olarak referanslar tarafından desteklenmeyen işaretçi aritmetik işlemlerini destekleyip desteklemediğidir.
Cort Ammon - Reinstate Monica

8
Bir o Cort iddialarını kabul @JimmyJames ben hemfikir işaretçi bir ederken, aritmetik işlemleri yapabileceği bir şeydir referans değildir. Java gibi dillerde bir başvuru gerçekleştiren gerçek mekanizma bir uygulama detayıdır.
Robert Harvey

3
Genel olarak, C ve C ++, spesifikasyona birçok "tanımlanmamış davranış" a izin vererek bu tehlikeli kulübe üyeliği gönüllü olarak kabul etmişti.
rwong

2
Bu arada, orada olan işaretçiler ve sayılar arasında ayrım CPU'lar. Örneğin, IBM AS / 400'deki orijinal 48 bit CISC CPU bunu yapar. Ve aslında, sadece işlemci numaraları ve işaretçiler arasında ayrım ve işaretçileri aritmetik korusun yok vasıta OS, altından bir soyutlama katmanı vardır, ama işletim sistemi kendisi bile işaretçiler bilmediği hiç dilleri yapmak ve ne . İlginç bir şekilde, bu, orijinal AS / 400'ü, C'deki yüksek seviyeli bir kodlama dilinden yeniden kod yazmanın, büyüklük derecelerini yavaşlattığı bir sistem haline getirir .
Jörg W Mittag

10

Karmaşık programlar için (örneğin özyinelemeli veya değişken boyutlu veri yapıları) bir çeşit dolaylı yayın gereklidir. Bununla birlikte, bu indirgeyi işaretçiler aracılığıyla uygulamak gerekli değildir.

Üst düzey programlama dillerinin çoğunluğu (örn. Montaj değil), bellek açısından güvenlidir ve sınırsız işaretçi erişimine izin vermemektedir. C ailesi burada garip.

C ham toplantının üzerinde çok ince bir soyutlama olan B'den çıktı. B'nin tek bir türü vardı: kelime. Sözcük bir tamsayı veya işaretçi olarak kullanılabilir. Tüm bellek tek bir bitişik dizi olarak görüntülendiğinde bu ikisi eşdeğerdir. C bu oldukça esnek yaklaşımı korudu ve doğal olarak güvensiz işaretçi aritmetiğini desteklemeye devam etti. Bütün C tipi sistem daha sonradan sonradan düşünülen bir sistemdir. Bellek erişimindeki bu esneklik C'yi birincil amacı için çok uygun hale getirdi: Unix işletim sisteminin prototipini oluşturma. Elbette Unix ve C'nin oldukça popüler olduğu ortaya çıktı, böylece C ayrıca belleğe bu düşük seviyeli yaklaşımın gerçekten gerekli olmadığı uygulamalarda da kullanılıyor.

C'den önce gelen programlama dillerine bakarsak (örn. Fortran, Algol lehçeleri, Pascal, Cobol, Lisp,… dahil), bunların bazıları C benzeri işaretçileri destekliyor. Özellikle, 1965'te Algol W için null işaretçi kavramı icat edildi. Ancak bu dillerden hiçbiri C benzeri, etkili bir düşük soyutlama sistemi dili olmaya çalışmadı: Fortran bilimsel hesaplama amaçlıydı, Algol oldukça gelişmiş kavramlar geliştirdi, Lisp endüstri düzeyinde bir dilden daha çok bir araştırma projesidir ve Cobol iş uygulamalarına odaklanmıştır.

Çöp toplama işlemi 50'li yılların sonlarından beri, yani C'den (70'lerin başı) çok önce vardı. GC'nin düzgün çalışması için bellek güvenliği gerekir. C öncesi ve sonrası diller GC'yi normal bir özellik olarak kullanmıştır. Tabii ki bu bir dili çok daha karmaşık ve muhtemelen daha yavaş yapar, bu da özellikle ana bilgisayarlar zamanında fark edilir. GC dilleri araştırmaya yönelik olma eğilimindedir (örn. Lisp, Simula, ML) ve / veya güçlü iş istasyonları gerektirir (örn. Smalltalk).

Daha küçük, daha güçlü bilgisayarlar genel olarak bilgisayar ve GC dillerinde özellikle daha popüler hale geldi. Gerçek zamanlı olmayan uygulamalar (ve hatta bazen de) için GC artık tercih edilen yaklaşımdır. Ancak GC algoritmaları da yoğun araştırmaların konusu olmuştur. Alternatif olarak, GC olmadan daha iyi bellek güvenliği daha da geliştirilmiştir, özellikle de son otuz yılda: C ++ ve Rust'un ömür boyu sistem / ödünç denetleyicisindeki RAII ve akıllı işaretçilerdir.

Java, bellek güvenli bir programlama dili olarak yenilik yapmadı: temelde GCed, bellek güvenli Smalltalk dilinin semantiğini aldı ve bunları C ++ sözdizimi ve statik yazımıyla birleştirdi. Daha sonra daha iyi, daha basit bir C / C ++ olarak pazarlandı. Ama bu sadece yüzeysel olarak bir C ++ soyundan geliyor. Java'nın işaretçi eksikliği, C ++ veri modelinin reddedilmesinden çok Smalltalk nesne modeline borçludur.

Dolayısıyla, Java, Ruby ve C # gibi “modern” diller, C'deki gibi ham işaretçilerin sorunlarının üstesinden gelmek olarak yorumlanmamalı, ancak C dahil birçok gelenekten değil, Smalltalk, Simula gibi daha güvenli dillerden çizim olarak görülmelidir. veya Lisp.


4

Deneyimlerime göre, işaretçiler DAİMA birçok insan için zorlu bir kavram olmuştur. 1970 yılında, katıldığım üniversitenin Burroughs B5500'ü vardı ve programlama projelerimiz için Genişletilmiş Algol'u kullandık. Donanım mimarisi, tanımlayıcılara ve veri kelimelerinin üst kısmındaki bazı kodlara dayanıyordu. Bunlar açıkça dizilerin uçtan çıkmasına izin verilmeden işaretçiler kullanmasına izin vermek için tasarlanmıştır.

İsim ve değer referansı ve B5500 dizilerinin nasıl çalıştığı ile ilgili sınıf içi tartışmalar yaptık. Bazılarımız açıklamayı hemen aldık. Diğerleri yapmadı.

Daha sonra, donanımın beni özellikle montaj dilinde kaçak işaretçilerden korumaması biraz şok oldu. Mezuniyet sonrası ilk işimde, bir işletim sistemindeki sorunların çözülmesine yardımcı oldum. Sık sık elimizdeki tek belge basılı kaza dökümü oldu. Hafıza dökümlerinde kaçak işaretçilerin kaynağını bulmak için bir ustalık geliştirdim, bu yüzden herkes bana "imkansız" dökümleri verdi. Karşılaştığımız sorunların çoğu, diğer hata türlerinden daha çok işaretçi hatalarından kaynaklanmıştır.

Birlikte çalıştığım insanların çoğu FORTRAN'ı yazmaya başladı, sonra C'ye taşındı, FORTRAN'a çok benzeyen C yazdı ve işaretçilerden kaçındı. İşaretçileri ve referansları hiçbir zaman içselleştirmedikleri için Java sorun çıkarır. Genellikle, FORTRAN programcıları için Nesne atamasının gerçekten nasıl çalıştığını anlamak zordur.

Modern diller, yazım hatalarından ve diğer hatalardan korurken, "kaputun altında" işaretçiler gerektiren şeyleri yapmayı çok daha kolaylaştırdı.

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.