Asıl sebep, bir yandan C ve C ++ ile diğer yandan Java ve C # (sadece birkaç örnek için) arasındaki niyet açısından temel bir farktan kaynaklanıyor. Tarihsel nedenlerden dolayı, buradaki tartışmaların çoğu C ++ 'dan ziyade C hakkında konuşur, fakat (muhtemelen zaten bildiğiniz gibi) C ++ C'nin oldukça doğrudan bir soyundandır, bu yüzden C hakkında söyledikleri eşit olarak C ++' a uygulanır.
Büyük ölçüde unutulmuş olmalarına rağmen (ve varlıkları bazen reddedilmiş olsalar da), UNIX'in ilk sürümleri assembly dilinde yazılmıştı. (Yalnızca değilse) C'nin asıl amacının çoğu, montaj dilinden daha yüksek bir dile kadar UNIX bağlantı noktasıydı. Niyetin bir kısmı, işletim sisteminde mümkün olan en yüksek düzeyde bir dilde yazmaktı - ya da diğer yönden bakmak, derleme dilinde yazılması gereken miktarı en aza indirmekti.
Bunu gerçekleştirmek için, C'nin donanıma derleme diliyle neredeyse aynı düzeyde erişim sağlaması gerekiyordu . PDP-11 (bir örnek için) G / Ç kayıtlarını belirli adreslere eşledi. Örneğin, sistem konsolunda bir tuşa basılıp basılmadığını kontrol etmek için bir hafıza konumunu okudunuz. Okunmayı bekleyen veriler olduğunda bu yere bir bit ayarlandı. Daha sonra, basılan tuşun ASCII kodunu almak için belirtilen başka bir konumdan bir bayt okudunuz.
Aynı şekilde, bazı veriler yazdırmak isterseniz, belirtilen başka bir yeri kontrol eder ve çıkış cihazı hazır olduğunda verilerinizi belirtilen başka bir yere yazarsınız.
Bu tür aygıtlar için yazma sürücülerini desteklemek için C, bazı tam sayı türlerini kullanarak rasgele bir konum belirtmenize, bir işaretçiye dönüştürmenize ve bu konumu bellekte okumanıza veya yazmanıza izin verdi.
Elbette, bunun oldukça ciddi bir sorunu var: dünyadaki her makine 1970'lerin başlarından itibaren bir PDP-11 ile aynı şekilde düzenlenmiş bir hatıraya sahip değil. Böylece, bu tamsayıyı aldığınızda, bir işaretçiye dönüştürdüğünüzde ve o işaretçiyi kullanarak okuduğunuzda veya yazdığınızda, ne alacağınız konusunda hiç kimse makul bir garanti veremez. Sadece bariz bir örnek için, okuma ve yazma, donanımdaki kayıtları ayırmak için haritalandırabilir, bu nedenle bir şey yazarsanız (normal belleğe aykırı), sonra tekrar okumaya çalışın, okuduklarınız yazdıklarınızla eşleşmeyebilir.
Bırakan birkaç olasılık görebiliyorum:
- Tüm olası donanıma bir arabirim tanımlayın - donanımla etkileşim kurmak için herhangi bir şekilde okumak veya yazmak isteyebileceğiniz tüm konumların mutlak adreslerini belirtin.
- Bu erişim seviyesini yasaklayın ve böyle şeyler yapmak isteyen herkesin assembly dilini kullanması gerektiğine dair karar verin.
- İnsanların bunu yapmasına izin verin, ancak hedefledikleri donanımın kılavuzlarını okumalarını (örneğin) kullanmalarını sağlayın ve kullandıkları donanıma uygun kodu yazın.
Bunlardan 1, yeterince tartışmaya değmeyecek kadar akıllıca görünüyor. 2 temel olarak dilin temel amacını ortadan kaldırıyor. Bu, üçüncü seçeneği, aslında makul bir şekilde düşünebilecekleri tek seçenek olarak bırakır.
Oldukça sık görülen bir diğer nokta ise tamsayı tiplerinin boyutlarıdır. C int
, mimarlık tarafından önerilen doğal boyutta olması gereken "konumu" alır . Öyleyse, eğer 32 bit bir VAX programlıyorsam, int
muhtemelen 32 bit olmalı, ancak 36 bit bir Univac programlıyorsam, int
muhtemelen 36 bit olmalıdır (vb.). Muhtemelen, 8 bit'in katları olarak garanti edilen türleri kullanarak 36 bitlik bir bilgisayar için bir işletim sistemi yazmak makul değildir (ve mümkün olmayabilir). Belki de sadece yüzeysel oluyorum, ama bana öyle geliyor ki 36 bitlik bir makine için bir işletim sistemi yazıyorsam, muhtemelen 36 bitlik bir türü destekleyen bir dil kullanmak istiyorum.
Dil açısından bakıldığında, bu hala daha tanımsız davranışlara yol açmaktadır. 32 bite uyacak en büyük değeri alırsam, 1 eklediğimde ne olacak? Tipik 32 bit donanımda, devrilecek (veya muhtemelen bir tür donanım arızası atacak). Öte yandan, 36 bit donanım üzerinde çalışıyorsa, sadece bir tane ekleyeceğim. Dil yazma işletim sistemlerini destekleyecekse, her iki davranışı da garanti edemezsiniz - hem tür boyutlarının hem de taşma davranışının birinden diğerine değişmesine izin vermek zorundasınız.
Java ve C # bunların hepsini görmezden gelebilir. Yazma işletim sistemlerini desteklemeyi amaçlamıyorlar. Onlarla birlikte birkaç seçeneğiniz var. Birincisi, donanımın istediklerini desteklemektir - 8, 16, 32 ve 64 bit türlerini talep ettikleri için, sadece bu boyutları destekleyen donanımlar oluştururlar. Diğer bir belirgin olasılık, dilin sadece istediği ortamı sağlayan, diğer donanımın ne istediğinden bağımsız olarak çalışabilmesidir.
Çoğu durumda, bu gerçekten bir seçenek veya seçenek değildir. Aksine, birçok uygulama ikisinden de biraz fazlasını yapar. Java'yı normalde bir işletim sisteminde çalışan bir JVM'de çalıştırın. Çoğu zaman, işletim sistemi C ile yazılmamıştır ve JVM C ++ ile yazılmıştır. Eğer JVM bir ARM CPU üzerinde çalışıyorsa, donanımı Java'nın ihtiyaçlarına daha yakından uyarlamak için CPU'nun ARM'in Jazelle uzantılarını içermesi ihtimali oldukça iyidir, bu yüzden yazılımda daha az yapılması gereken ve Java kodunun daha hızlı çalışması (ya da daha az olması) neyse yavaşça).
özet
C ve C ++ tanımsız davranışa sahiptir, çünkü kimse yapmayı amaçladıklarını yapmalarına izin veren kabul edilebilir bir alternatif tanımlamamıştır. C # ve Java farklı bir yaklaşım benimsiyor, ancak bu yaklaşım C ve C ++ hedeflerine (hiç değilse) çok yakışmıyor. Özellikle, hiçbiri en çok seçilen donanım üzerine sistem yazılımı (işletim sistemi gibi) yazmak için makul bir yol sağlamaz. Her ikisi de, işlerini yapmak için mevcut sistem yazılımı tarafından sağlanan tesislere (genellikle C veya C ++ ile yazılmış) bağlıdır.