Programlama işindeki ilerlemeyi satır sayısı ile ölçmek, uçak imalatı işindeki ilerlemeyi ağırlıkla ölçmek gibi olur.

Bill Gates

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
30
2009

Nedir Şu POD Meselesi?..

    POD (Plain Old Data) C++’ta ayrıntıları pek bilinmeyen karışık bir konudur. Pek çok dökümanda POD terimi geçtiği halde pek az C++ programcısının bu konuyu tam olarak bildiğini görüyorum.

    C’de genel olarak (ister int, long gibi aritmetik türden olsun, isterse dizi ya da yapı türünden olsun) her türden nesnenin byte’ları bellekte ardışıl bir biçimde tutulmaktadır. Nesnenin adresini alarak o adresten itibaren sizeof değeri kadar byte’ı memcpy gibi bir fonksiyonla bir alana aktarıp aynı biçimde geri alırsak eski nesneyi elde ederiz. C standartları bunu garanti etmektedir. Örneğin T herhangi bir türü belirtmek üzere:

#define N sizeof(T)
char buf[N];
T obj;
...

memcpy(buf, &obj, N);
memcpy(&obj, buf, N);


Fakat C++ bunu garanti etmiyor. Yani C++’ta her türden nesnenin bellekte ardışıl byte topluluğu olarak bulunması garanti değil. C++ standartları yalnızca POD diye tanımladığı türlere ilişkin nesnelerin byte’larının bellekte ardışıl olacağını garanti ediyor. Yani biz C’de herhangi bir nesneyi bir dosyaya tek hamlede fwrite gibi bir fonksiyonla aktarıp, fread gibi bir fonksiyonla geri alabilirken C++’ta bunu yalnızca POD özelliği taşıyan türler için yapabiliriz. POD sözcüğü “Plain Old Data” sözcüklerinden kısaltmadır. POD C++’ta tıpkı C’deki gibi byte’ları bellekte ardışıl olarak bulunmak zorunda olan nesneleri belirtmek için kullanılıyor.

    Peki C++’ta POD tanımı nasıl yapılmış? Standartlar POD türlerini şöyle tanımlıyor: “Skaler türler (yani aritmetik türler ve adres türleri), POD-struct ve POD-union türleri, bu türlerden diziler ve bunların const-volatile biçimleri POD türlerdir.” Yani standartlara göre int, long, int *, long *, int türden, long türden diziler POD nesnelerdir. Fakat gördüğünüz gibi standartlar "her sınıf (yapılar da sınıftır) POD türündendir" demiyor. Yalnızca POD-struct ve POD-union türlerinin POD olduğunu söylüyor. O halde bizim POD-struct ve POD-union türlerinin ne olduğunu anlamamız gerekiyor.

 Standartlarda bir türün POD-struct olması için şu koşullar öngörülmüş:

- POD-struct bir toplaşık (aggregate) sınıftır.
- POD-struct static olmayan (non-static) POD-struct, POD-union türünden olmayan veri elemanına ve referans veri elemanına sahip olamaz.
- POD-struct programcı tarafından tanımlanmış kopya atama operatör fonksiyonuna ve bitiş fonksiyonuna (destructor) sahip olamaz (toplaşık sınıflar zaten başlangıç fonksiyonuna sahip olamaz).

POD-union için de benzer koşullar öngörülmüş:

- POD-union bir toplaşık birliktir.
- POD-union static olmayan (non-static) POD-struct, POD-union türünden olmayan veri elemanına ve referans veri elemanına sahip olamaz.
- POD-union programcı tarafından tanımlanmış kopya atama operatör fonksiyonuna ve bitiş fonksiyonuna (destructor) sahip olamaz (toplaşık sınıflar zaten başlangıç fonksiyonuna sahip olamaz).

Toplaşık tür (aggregate type) olma koşulları şöyledir:

- Sınıf ya da dizi türleri toplaşık türlerdir.

 Eğer sınıf türü söz konusuysa:

- Sınıfta kullanıcının bildirdiği bir başlangıç fonksiyonu olmamalıdır.
- Sınıfta private ya da protected bölümde bildirilmiş static olmayan veri elemanı bulunmamalıdır.
- Sınıfın taban sınıfı olmamalıdır.
- Sınıfın sanal fonksiyonu bulunmamalıdır.

Bu kadar çok tanımlamalardan sonra aklınızın karışmaması olanaksız :-). O halde ben konuyu ayrıntıları gözardı ederek şöyle basitleştireyim: Bir sınıfın ya da birliğin POD olabilmesi için onun taban sınıfının olmaması, sanal fonksiyonunun olmaması, programcı tarafından tanımlanan bir başlangıç fonksiyonunun, kopya atama operatör fonksiyonunun ve bitiş fonksiyonunun olmaması gerekir. Ayrıca POD bir sınıf ya da birlik yalnızca POD türden static olmayan public veri elemanlarına sahip olabilir. Zaten böyle bir sınıf C’deki yapı gibi bir sınıftır. Böyle sınıflar türünden nesnelerin byte’ları ardışıl olmak zorundadır. 

    POD kavramının ilkdeğer verilme işlemi üzerinde de etkisi vardır. Standartlara göre sınıf nesnesi yaratılırken eğer parantezler kullanılmamışsa ve eğer sınıf POD değilse nesne için sınıfın default başlangıç fonksiyonu çağrılır. Fakat sınıf POD ise, herhangi bir ilkdeğerleme işlemi yapılmaz.

X a;
X *p = new X;

Burada eğer X,  POD ise sınıfın default başlangıç fonksiyonu çağrılır fakat POD değilse hiçbir başlangıç fonksiyonu çağrılmaz.

    Gördüğünüz gibi standartlara göre C++’ta bir sınıfın elemanları ardışıl bir biçimde bulunmak zorunda değildir. Gerçekten de standartlarda sınıfların anlatıldığı bölümde sınıfın iki erişim belirleyici anahtar sözcüğü arasındaki kısmın ardışıl olması gerektiği belirtilmiş fakat bütünsel olarak bu garanti verilmemiştir. Fakat standartlar pek çok olası ortam göz önüne alınarak hepsinin içerebileceği ortak özellikler bağlamında oluşturulmuştur. Pratikte baktığımızda ise yaygın olarak kullanılan derleyicilerin hepsinin sınıfların veri elemanlarını ardışıl bir biçimde yerleştirdiğini görmekteyiz. (Bu nedenle standartlardaki durumu fazlasıyla ciddiye alıp bu konuyu bilgiçlik taslama amacıyla kullanmayınız :-) ). En iyisi ben size şöyle yardımcı olayım: Siz elinizdeki sınıfa ilişkin nesnenin byte’larını ardışıl varsayın. Ben kefilim. Eğer bu nedenden dolayı sorun çıkarsa bana bildirin, ben çözmeyi garanti ediyorum :-).
 
    Ayrıca C++0x'te POD kavramının biraz daha genişletildiğini belirteyim. Örneğin taban sınıf POD ise türemiş sınıfın POD olabilmesi gerekirdi. Fakat C++2003 bunu yasaklamış. (C++0x'teki POD kavramı hakkında geçen sene makale formunda birşeyler yazmıştım. Onu da yakında makaleler kısmına yerleştiririm.)

Haftanın Böceği Yukarı