Teknolojik ilerleme, patolojik bir suçlunun elindeki balta gibidir.

Albert Einstein

Bana Atılan E-Postaların Yanıtları

Sevgili Arkadaşlar...

Bu site aracılığıyla -bazıları sorulardan oluşan- pek çok e-posta alıyorum. Ancak yoğunluk nedeniyle bazılarına yanıt yazamadım.  Sakın unuttum sanmayın ve "adam bir yanıt bile yazmadı" diye düşünmeyin :-). Bundan sonra atacağınız e-postalara da daha kısa bir süre içinde yanıt vermeye çalışacağım. Gecikme için özür diliyorum...

 

Lütfen Alıntılarda Kaynak Belirtiniz

Çeşitli Internet sitelerinde yazmış olduğum makalelerden, ders notlarından, çizimlerden, kaynak kodlardan vs. bire bir alıntı yapıldığını, ancak kaynak belirtlmediğini görüyorum. Lütfen bu gibi alıntlarda kaynak belirtiniz.

Kitap Eleştirisi

Artık her hafta sitede bilgisayar alanında basılmış bir kitabın eleştirisini göreceksiniz. Umarım beğenirsiniz...

Tarihe Göre

Yeni İçerik

Proseslerin Kod, Data, BSS, Stack ve Heap Alanları

Prosesler işletim sistemlerinin sundukları sistem fonksiyonlarıyla yaratılırlar. Örneğin, Windows sistemlerindeki CreateProcess, UNIX/Linux sistemlerindeki fork proses yaratan fonksiyonlardır. Windows ve UNIX/Linux sistemlerindeki proses yaratımları arasında önemli bir fark vardır. Windows sistemlerindeki CreateProcess fonksiyonu çalıştırılabilen (executable) bir dosyadan hareketle prosesi oluşturur. (Yani yaratılacak proses yol ifadesini argüman olarak verdiğimiz programın kodlarını çalıştıracaktır.) Halbuki UNIX/Linux sistemlerindeki fork fonksiyonu prosesin özdeş bir kopyasını oluşturmaktadır. Bu sistemlerde başka bir programın kodları ancak exec türevi fonksiyonlarla çalıştırılabilir. Fakat programcı bu sistemlerde doğrudan exec fonksiyonunu kullanırsa çalışmakta olan mevcut programın bellek alanı değiştirilerek proses yaşamına başka bir kodla devam eder. Bu nedenle UNIX/Linux sisteminde hem mevcut programı sürdürmek hem de yeni bir programı çalıştırmak istiyorsak fork ve exec fonksiyonlarını beraber kullanmamız gerekir. Bu sağlamak için de, tipik olarak, önce bir kez fork yaparız, alt proseste exec uygularız. Windows sistemlerindeki CreateProces fonksiyonu UNIX/Linux sistemlerindeki fork ve exec fonksiyonlarının bileşimine benzetilebilir.

Portable Executable Dosya Formatı

Microsoft, DOS işletim sisteminde çalıştırılabilen dosya formatı olarak MZ formatını kullanıyordu.[1] MZ formatı koruma mekanizması olmayan 8086 mimarisi için tasarlanmıştı ve korumalı modun gereksinim duyduğu özelliklere sahip değildi.  Bu nedenle Microsoft 16 bit Windows sistemleriyle birlikte çalıştırılabilir dosya formatını NE (New Executable) ismiyle, 32 bit Windows sistemleriyle de PE (Portable Executable) ismiyle yenilemiştir. Bugün 32 bit ve 64 bit Windows sistemlerinde çalıştırılabilen dosya formatı olarak PE formatı kullanılmaktadır. PE formatı UNIX türevi sistemlerde kullanılan ELF (Executable and Linkable Format) formatı gibi, işlemciden bağımsız olan ve bölümlerden (sections) oluşan bir formattır. Microsoft .NET ortamıyla birlikte PE formatı üzerinde bu ortamın gereksinimlerinin karşılanması için çeşitli eklentiler de yapmıştır. PE formatının orijinali 32 bittir. Fakat daha sonraları 64 bit sistemler için bu formatın 64 bit versiyonu da oluşturulmuştur. PE formatının 64 bitlik bu versiyonuna PE+ da denilmektedir.

Dinamik Diziler (Veri Yapıları 2. Bölüm)

Programlama dillerinin çoğunda diziler bir kere yaratıldıktan sonra artık büyütülemezler ve küçültülemezler. Oysa bazı durumlarda açacağımız dizinin uzunluğunu işin başında bilemeyiz. Programın çalışma zamanı sırasında onların dinamik bir biçimde büyütülmesini ya da küçültülmesini isteyebiliriz. Programın çalışma zamanı sırasında büyütülebilen ya da küçültülebilen dizilere dinamik diziler (dynamic arrays) denilmektedir.[1]  Dinamik diziler C, C++, Java ve C# gibi dillerde temel bir veri yapısı değildir. Yani bu dillerde dinamik diziler dilin sentaksı tarafından doğrudan desteklenmezler. Bunların fonksiyonlar ya da sınıflar kullanılarak gerçekleştirilmeleri gerekir. Ancak Perl ve Ruby gibi dinamik dizilerin dilin sentaksı tarafından temel bir veri türü olarak desteklendiği diller de vardır.

Önbellek (Cache) Sistemleri

Bilgisayar sistemlerinde pek çok durumda erişim hızı bakımından iki tür belleğin söz konusu olduğunu söyleyebiliriz: Yavaş bellek ve hızlı bellek. Yavaş bellek ucuz ve boldur, hızlı bellek ise pahalı ve kıttır. Tabi buradaki yavaş ve hızlı kavramları görelidir.  Sistemden sisteme yavaş ve hızlı bellekler farklılaşabilir. Örneğin bir sistemde RAM hızlı belleği temsil ederken disk yavaş belleği temsil ediyor olabilir. Başka bir sistemde ise RAM yavaş belleği temsil ederken işlemci içerisinde bulunan bellek hızlı belleği temsil ediyor olabilir. Ya da bir web sayfasının sunucuda saklandığı yer yavaş belleği temsil ederken onun istemcide bir dosyada saklanmış hali yavaş belleği temsil ediyor olabilir. Önbellek (cache) yavaş belleğin belli bölümlerinin hızlı bellekte tutularak yavaş belleğe erişim oranını azaltmak için kullanılan bir bellek yönetim tekniğidir. Önbellek sistemleri donanımsal ya da yazılımsal bir biçimde ya da karma bir biçimde gerçekleştirilebilmektedir.

UNIX/Linux Sistemlerinde Boru Haberleşmeleri

Boru haberleşmeleri yalınlığından dolayı en çok tercih edilen prosesler arası haberleşme yöntemlerindendir. Yöntem kendi içerisinde eşzamanlılığı da barındırdığından programcının ayrıca eşzamanlılık sorunuyla uğraşmasına gerek kalmaz. Boru haberleşmeleri ilk UNIX sistemlerinden beri neredeyse tüm UNIX türevi sistemlerce desteklenmiştir. Yöntem başta Windows olmak üzere pek çok işletim sisteminde de benzer biçimde uygulanmaktadır.

Temel Veri Yapıları (Veri Yapıları 1. Bölüm)

Programlarımızda tanımladığımız nesneler ya tek parçadan ya da birden fazla parçadan oluşurlar. Tek parçadan oluşan nesnelerin türlerine tekil türler, birden fazla parçadan oluşan nesnelerin türlerine ise bileşik türler denilmektedir. Örneğin, int türü tekil bir türdür. Çünkü int türden bir nesne tek parçadan oluşmaktadır. Halbuki diziler bileşik türlerdir. Bir dizi türünden nesne tanımladığımızda o nesne kendi içerisinde birden fazla parçadan oluşmaktadır. Örneğin:

C'de Bağlanım (Linkage) Nedir, Ne Değildir?

C'de kullanılan her değişken (identifier) için bir bildirimin yapılmış olması gerekir. Bağlanım (linkage) farklı bildirimlerdeki aynı isimli değişkenlerin aynı nesneyi ya da fonksiyonu belirtip belirtmediğini anlatan bir özelliktir. C'de değişkenlere ilişkin üç  bağlanım durumu söz konusudur:

Mayıs
26
2009

Programlama Dillerindeki Kuralların Gerekçeleri

    Doğal dillerdeki kurallar matematiksel anlamda kesin değildir, bunların istisnaları çoktur. Zaten bizi herhangi bir şey öğrenirken bezdiren şeylerden biridir istisnalar. Örneğin İngilizce’de daha yüksek demek için high sıfatı higher haline, tall sıfatı taller haline geliyor. Pek çok sıfatın bu biçimde "er" eki aldığını görüyoruz. Fakat bu kesin bir kural mı? Hayır, bunun pek çok istisnası var. Örneğin, (her nedense) kötü bad olduğu halde daha kötü için worse kullanılıyor. (Ana dil gibisi var mı? Hepimiz kırmızı için kıpkırmızı, mavi için masmavi denildiğini biliriz. Bunun bir kuralının olup olmadığını sorgulamamışızdır. Zaten umurumuzda da değildir. Kimse kırmızı için maskırmızı demez.)

    Fakat programlama dillerinin kesin kuralları ve bu kuralların da çeşitli gerekçeleri vardır. (Örneğin “case ifadeleri sabit ifadesi olmak zorundadır” kuralının gerekçeleri vardır ve ben bunu geçen haftalarda açıklamıştım.) Çünkü programlama dilleri doğal diller gibi uzun bir süre içerisinde toplumsal bir yaşantıyla ortaya çıkmıyor. Bunlar mantık temelinde bilinçli olarak tasarlanıyorlar. Herşeyden önemlisi programlama dillerinin sentaks yapıları matematiksel olarak açıklanabiliyor. Bu nedenle bu tür dillere biçimsel diller (formal languages) denilmekte. 

    Programlama dillerindeki bazı kuralların bazı gerekçeleri o dili kullanan herkes tarafından tahmin edilebilir. Fakat bazı gerekçelerin anlaşılması ve kavranması yüksek bir bilinç düzeyini gerektirir. Neden mi? Çünkü bazı kuralların gerekçelerini anlayabilmek için başka şeylerin bilinmesi gerekir de ondan. Örneğin, pek çok programlama dilinde goto ile başka bir fonksiyona atlama yapılamaz.  Bu kuralın gerekçesini stack mekanizmasını bilmeyen bir kişiye nasıl açıklayabiliriz? 
    
    Bazı programlama dillerinde kuralların gerekçeleri resmi dökümanlar halinde açıklanmaktadır. Örneğin C90 ve C99 için özel hazırlanmış gerekçeler (rationale) eki resmi bir açıklama niteliğindedir. Bazı programlama dillerinde ise bazı gerekçelerin standartların kendi içerisinde doğrudan açıklandığını görürüz. Örneğin, C#’ta bazı kuralların gerekçeleri bu biçimde standartların içerisinde belirtilmiş durumda. Peki genel olarak bir kuralın gerekçesini nasıl öğrenebiliriz? Öncelikle eğer varsa resmi dökümanlara bakmanızı tavsiye ederim. Eğer böyle bir döküman yoksa  bu durumda tasarımcıların hazırladıkları kitaplar, makaleler ya da onlarla yapılan röportajlar ve söyleşiler iyi bir kaynak olabilirler. Örneğin, Stroustrup’un “The Annotated C++ Reference Manual” isimli kitabı -eski olmasına karşın- C++ için iyi bir gerekçe açıklama dökümanı olarak kullanılabilir. Ya da örneğin C++/CLI için “A Design Rationale for C++/CLI” makalesini kullanabilirsiniz. USENET haber grupları, tartışma grupları, blog’lar yine kaynak oluşturabilirler. Zaten görüyorsunuz, ben de kendi blog’umda arada sırada bazı gerekçeleri açıklamaya çalışıyorum. 

    Peki ya tasarımcılar tarafından oluşturulan kurallar ve bunların gerekçeleri her zaman bizi ikna edebiliyor mu? Yanıt hayır! Bu bağlamda ben pek çok dildeki pek çok kuralı gerekçeleriyle birlikte eleştiriyorum. Örneğin, C#'taki switch deyiminde aşağıya düşme kuralının (fall through) kaldırılarak bunun dolaylı bir biçimde goto case ve goto default deyimleriyle karşılanmasını gereksiz ve saçma buluyorum. Bu konuda Hejlsberg'in belirttiği gerekçeler de beni tatmin etmiyor. Tabi gerekçeleri gözden geçirirken o programlama dilinin seviyesini, kullanım alanını ve programlama modelini göz önüne almak zorunda olduğumuzun farkındayım. Fakat siz de bazen "neden şöyle değil de böyle" diye merak ettiğiniz kuralların pekala "şöyle" de olabileceğini gözardı etmeyin. Çünkü bazı durumlarda tasarımcının elinde birbirine yakın birden fazla seçenek vardır. Bu seçenekler neredeyse eşdeğer işlevselliktedir. Tasarımcı da bunlardan birini seçmiştir.

    Programlama dillerindeki bazı kurallar da zamanla değiştirilebiliyor. Buna tipik bir örnek olarak C++’taki deyimlerin parantezleri içerisinde bildirilen değişkenlerin faaliyet alanı verilebilir:

for (int i = 0; i < 100; ++i) {
    //...
}

i değişkeni nerede bildirilmiştir? Stroustrup önce bunun for döngüsünün yerleştirildiği blokta bildirilmiş olması gerektiğini düşünmüştür. Fakat bu kural yeterince işlevsel değildir. Yukarıdaki döngüyü biraz aşağıya kopyalarsanız hata oluşur. Bu kural daha sonra (standartlarla birlikte) değiştirilmiştir. Bugün artık C/C++ kökeninden gelen pek çok dilde bu değişkenlerin faaliyet alanları döngü bloğu ile sınırlıdır.

    Programlama dillerinin tasarımında ve standartlarında böcek olmuyor mu? Tabi oluyor. Fakat bunları zamanla düzeltiyorlar. Bugün Internet sayesinde çeşitli ortamlarda pek çok yeni fikir tartışılabiliyor ve eski fikirler eleştirilebiliyor. Tabi geçmişe doğru uyumun bozulması bazen tüm çabalara rağmen engellenemiyor...

Haftanın Böceği Yukarı