Bir yazılım onu kullanan en son kullanıcı ölünceye kadar yaşar.

Anonim

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:

Ağustos
15
2009

restrict Gösterici Nedir ve Ne İşe Yarar?

    restrict gösterici kavramı resmi olarak C99 ile standartlara girdi. C++ standardizasyon komitesi restrict göstericilere hiç sıcak bakmadı. C++0x’te de restrict göstericilerin eklenmesine yönelik bir işaret yok.

    restrict göstericiler aynı nesneyi gösteren göstericilerin (pointer aliasing) yol açtığı optimizasyon engelini ortadan kaldırmak için düşünülmüştür. Aynı nesneyi gösteren göstericilere yönelik optimizasyonları Derleyicilerin Kod Optimizasyonları 2. Bölüm başlıklı makalede açıklamıştım. Önce kısa bir tekrar yapayım.
 
    Göstericinin gösterdiği yere erişmek pek çok işlemcide birden fazla makina komutunu gerektirmektedir. Bu nedenle derleyiciler optimizasyon amacıyla her defasında göstericinin gösterdiği yere erişmek yerine göstericinin gösterdiği yerdeki nesneyi geçici bir yerde tutup oradan kullanmak isteyebilirler. Fakat derleyicilerin böyle bir optimizasyonu yapabilmesi için göstericinin gösterdiği yerdeki nesnenin başka bir yolla değişmeyeceğinden emin olmaları gerekir. Örneğin:

void foo(int *p)
{
    int i;

    for (i = 0; i < 100; ++i)
        bar(*p * 2);
}

burada derleyici *p’yi bir yerde saklayıp onu kullanarak hız kazancı sağlayabilir mi? Örneğin bu kod aşağıdaki ile eşdeğer midir?

void foo(int *p)
{
    int i;

    int temp = *p;
    for (i = 0; i < 100; ++i)
        bar(temp * 2);
}


Hayır. Çünkü p global bir nesneyi gösteriyor olabilir ve bar fonksiyonu da bu nesneyi değiştiriyor olabilir. Derleyici böyle bir olasılığı göz ardı edemez. Peki derleyici bar fonksiyonunun p’nin gösterdiği yerdeki nesneyi değiştirmediğinden nasıl emin olabilir? Eğer derleyici bar fonksiyonunun kodlarını görürse (başka bir modüldeyse göremeyebilir) bunu anlayabilir. Tabi derleyici için bunun hiç de kolay olmadığını söylemeliyim. Şimdi blok kopyalaması yapan memcpy benzeri bir fonksiyon yazmak isteyelim. Böyle bir fonksiyonun klasik yazımı aşağıdaki gibidir:

void *copymem(void *dest, const void *source, size_t size)
{
    char *cdest = (char *) dest;
    const char *csource = (const char *) source;

    while (size-- > 0)
        *cdest++ = *csource++;   
        
    return dest;
}

Derleyicinin yapılmak istenen şeyi farkettiğini varsayalım. Kopyalamayı byte byte değil de long long yaparak hız kazancı elde etmek istesin. Bu kodu aşağıdaki gibi derleyebilir mi?

void *copymem(void *dest, const void *source, size_t size)
{
    char *cdest = (char *) dest;
    const char *csource = (const char *) source;
    size_t count, remainder;

    count = size / sizeof(long);
    remainder = size % sizeof(long);

    while (count-- > 0) {
        *(long *) cdest = *(const long *) csource;
        csource += sizeof(long);
        cdest += sizeof(long);
    }
    while (remainder-- > 0)
        *cdest++ = *csource++;

    return dest;
}

Hayır. Çünkü bu iki kod eşdeğer değildir. Bloklar çakışmasa tamam ama ya çakışırsa?.. Örneğin:

copymem(p + 1, p, 100);

gibi bir çağrıda birinci fonksiyonla ikinci fonksiyonun davranışı tamamen farklıdır.

    İşte restrict göstericiler bir nesneye o göstericiden başka bir yolla erişilmeyeceği sözünü derleyiciye veriyorlar. Yani bir göstericiyi restrict yapmakla siz derleyiciye o göstericinin gösterdiği yerlere yalnızca o gösterici yoluyla erişileceğinin garantisini veriyorsunuz[1]. Şimdi yeniden birinci örneğe dönelim:

void foo(int * restrict p)
{
    int i;

    for (i = 0; i < 100; ++i)
        bar(*p * 2);
}


Burada derleyici artık p göstericisinin gösterdiği yerin başka bir biçimde değiştirilmeyeceğinden emin olur ve kodu aşağıdaki gibi iyileştirebilir:

void foo(int * restrict p)
{
    int i;

    int temp = *p;
    for (i = 0; i < 100; ++i)
        bar(temp * 2);
}


Şimdi de copymem örneğine dönelim:

void *copymem(void * restrict dest, const void * restrict source, size_t size)
{
    char *cdest = (char *) dest;
    const char *csource = (const char *) source;

    while (size-- > 0)
        *cdest++ = *csource++;   
        
    return dest;
}


Siz burada derleyiciye dest ve source göstericileriyle eriştiğiniz yerlere başka bir yolla erişmeyeceğiniz sözünü vermiş oluyorsunuz. O halde bu durumda bloklar da çakışık olamaz. Çünkü blokların çakışık olması onların gösterdiği yerlere başka biçimlerde erişebileceğiniz anlamına gelir. İşte derleyici bu garanti altında kodu optimize edebilir.

    restrict niteleyicisi yalnızca göstericiler için ve yalnızca göstericilerin kendisi için kullanılabilir. Göstericilerin gösterdiği yer restrict olamaz. Örneğin:

restrict int a;           // geçersiz!
restrict char *p2;        // geçersiz!
char * restrict p1;       // geçerli


restrict bir göstericinin yaşamı boyunca o gösterici ile erişilen nesnelere başka bir yolla erişilmemelidir. Eğer erişilirse tanımsız davranış (undefined behavior) oluşur. Böylece örneğin:

void *copymem(void * restrict dest, const void * restrict source, size_t size);

biçiminde bildirilmiş bir fonksiyona biz çakışık blok adresi geçemeyiz. Eğer geçersek programımız sağlıklı çalışmayabilir.

C99’da pek çok standart C fonksiyonunun prototipi değiştirilerek göstericiler restrict yapılmıştır. Göstericileri restrict yapılan bazı standart C99 fonksiyonları şunlardır:
 
FILE *fopen(const char * restrict filename, const char * restrict mode);
int fprintf(FILE * restrict stream, const char * restrict format, ...);
int fscanf(FILE * restrict stream, const char * restrict format, ...);
int printf(const char * restrict format, ...);
int scanf(const char * restrict format, ...);
int snprintf(char * restrict s, size_t n, const char * restrict format, ...);
int sprintf(char * restrict s, const char * restrict format, ...);
int sscanf(const char * restrict s, const char * restrict format, ...);
char *fgets(char * restrict s, int n, FILE * restrict stream);
int fputs(const char * restrict s, FILE * restrict stream);
size_t fread(void * restrict ptr, size_t size, size_t nmemb, FILE * restrict stream);
size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict stream);
int fgetpos(FILE * restrict stream, fpos_t * restrict pos);
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
char *strncpy(char * restrict s1, const char * restrict s2, size_t n);
char *strcat(char * restrict s1, const char * restrict s2);
char *strtok(char * restrict s1, const char * restrict s2);


[1] Tabi restrict bir gösterici kullandığınızda o göstericiyle erişebilme hakkınızın olduğu tüm yerlere başka bir biçimde erişmeme sözü veriyorsunuz. Örneğin restrict gösterici bir diziyi gösteriyorsa yalnızca o göstericinin gösterdiği elemana değil, o dizinin hiçbir elemanına başka bir yolla erişmeyeceksiniz.

Haftanın Böceği Yukarı