Standart C Programlama Dili


4.3.5. İlkleme

Bu altkısma başlamadan önce, gelin bellek sınıflarını bir daha gözden geçirelim: Otomatik (auto) sınıfından olan değişkenler ait oldukları blokla beraber ortaya çıkarlar ve sonra yok olurlar. Yazmaç (register) sınıfı değişkenler otomatikler gibidir; tek farkları yazmaçlara yerleştirilmelerinin söz konusu olmasıdır. Dural (static) yerel değişkenler değerlerini asla yitirmezler, oysa aynı sınıftan olan küresel değişkenler ve fonksiyonlar kendi dosyalarında gizli kalırlar. Dural (static) olmayan küresel değişkenler ve fonksiyonlar dördüncü ve son bellek sınıfını oluştururlar, bunlara dışsal adı verilir ve programın herhangi bir yerinden kullanılabilirler.

Programcı tarafından, aksi belirtilmediği sürece dışsal ve static bellek sınıfından olan değişkenler sıfıra ilklenir. auto ve register sınıfından olan değişkenlerin ilk değerleri belirsizdir.

Bildiğiniz gibi, tanım esnasında bir ilkleme yapmak için, değişkenin arkasına = işaretini koyup (çengelli parantez içine alarak veya almayarak) bir ifade yazarız. Böyle ifadeler için kurallar bellek sınıfına bağlı olarak değişir.

Dışsal veya static değişkenler durumunda, bu bir değişmez ifade olmalıdır ve şimdiye kadar gördüğümüz değişmez ifadelerden farklı olarak (adreslerini belirtmek için) dizi ve fonksiyon isimleri ve dışsal ile static değişkenler veya dizi elemanlarına uygulanan tekli & işleciyle oluşturulmuş ifadeler de içerebilir. Bütün bunların anlamı, böyle bir ifadenin değerinin (a) bir adres artı veya eksi bir değişmez veya (b) sadece bir değişmez olabileceğidir. Örnek:

float t = (900.9-888.1)*1.1;

Eğer ilklenecek değişken auto veya register ise ilkleme ifadesi daha önce tanımlanmış sözcükler içerebilen her çeşit geçerli C ifadesi olabilir. Örnek:

{
  double v = x/a()%77; /* x ve a bu blok içinde bilinmektedir. */
  bloğun geri kalanı
}

Gelin bu kuralların arkasındaki nedeni araştıralım:

Dışsal ve static değişkenler derleme sırasında hesaplanabilecek ilkleme ifadeleri isterler, çünkü bu esnada ilklenmektedirler. Program çalışmaya başlamadan, bu değişkenler ilk değerlerini almış olurlar. Bunun için, tanımında bir ilkleyeni olan yerel bir static değişken bloğa her girişte değil de sadece bir defa ilklenir. Bu da doğru bir şeydir, çünkü static bir değişken böyle davranmalıdır.

Diğer taraftan, derleyici, bir auto veya register değişkeni için bir ilkleyen gördüğünde, bunu o bloğun tanım listesinden hemen sonra gelen ve ilgili değişkenlere atama yapan bir deyim gibi görür. Bu atamalar, yürütme sırasında, bloğun başına ulaşıldığı her sefer tekrarlanır. Bundan dolayı, auto ve register değişkenleri için ilkleme ifadeleri, ilgili değişken bir sol işlenen olarak bir atama deyiminde kullanıldığı zaman sağ tarafta gelebilecek her tür geçerli ifade olabilir. Bu da, diğer dillerde rastlanması zor olan, C’nin güzel özelliklerinden biridir.

Artık, static olmayan değişkenlerin bir switch bloğunda ilklenmelerinin neden anlamsız olduğu anlaşılıyor: Her durumda, kontrol bu ilklemelerin üstünden geçer.

İlklenecek değişken bir dizi ise o zaman ne olur? Artan indis sırasına göre çengelli parantezler içine ve virgülle ayırarak her dizi üyesinin değerini belirten ifadeler yazılır. Bu listede, dizinin boyundan daha fazla sayıda ifadenin bulunması hata olur. Fakat eğer daha az sayıda ifade varsa, kalan dizi elemanları sıfıra ilklenir. Örneğin:

int g[5] = { 1, -2, 0, 3 };

dizinin ilk dört elemanına belirtilen değerleri atar, beşinciye de sıfır yerleştirir.

Dizi ilklenmesinde bazı kestirmeler söz konusu olabilir. Eğer dizinin tanımlanması esnasında boyunu belirtmezseniz, derleyici (mutlaka var olması gereken) ilkleme listesine bakar ve listedeki eleman sayısına eşit boyda bir dizi yaratır. Örneğin;

float f[] = { 2.2, 0.3, 99.9, 1.1 };

dört elemanlı bir dizinin ayrılmasını ve yukarıdaki değerlerle ilklenmesini sağlar.

Karakter dizileri liste yerine değişmez bir karakter dizisi yazarak da ilklenebilirler:

char gercek[] = "C iyidir.";

ile

char gercek[] = {
  'C', ' ', 'i', 'y', 'i', 'd', 'i', 'r', '.', '\0'
};

aynı anlama gelirler. İlk durumda belirtilmemiş olmasına rağmen derleyici tarafından otomatik olarak eklenen boş karaktere dikkat edin. Eğer böyle bir durumda dizinin uzunluğu belirtilmiş olsa ve ilkleyen karakter dizisinin uzunluğuna (yukarıdaki örnekte 9’a) eşit olsaydı, bitirici boş karakter diziye dahil edilmeyecekti.

C Standardına nispeten yeni eklenen bir özellik ile, dizilerin belirtilen elemanlarını ilklemek mümkündür. Aşağıdaki ilk örnekte, d isimli dizinin her bir çeyreğindeki ilk iki elemanı sırası ile 1 ve -1’e, 2 ve -2’ye, 3 ve -3’e, 4 ve -4’e, geri kalan elemanları da 0’a ilklenir. İkinci örnekte, e isimli 101 elemanlı bir dizi oluşturulur ve ilk 100 elemanı 0’a, geri kalan son elemanı ise -1’e ilklenir.

#define DU 100
int d[DU] = { 1, -1, [DU/4]=2, -2, [DU/2]=3, -3, [DU*3/4]=4, -4 };
int e[] = { [DU]=-1 };

Göstergeleri de ilkleyebilirsiniz. Otomatik veya register göstergeler için ilkleyen herhangi bir geçerli gösterge ifadesi olabilir. static veya dışsal göstergeler için değişmez bir ifade olmalıdır. Aşağıdaki örnekte,

#define  N  10

void test (int i)
{
  int         a[N], *ag = a+i;
  static int  b[N], *bg = &b[N],
    *bg1 = &b[0]+i,          /* hata */
    *bg2 = a;                /* hata */
  
}

ag ve bg için ilklemeler geçerlidir. Fakat bg1 için ilkleme geçersizdir, çünkü i bir değişmez değildir. Ayrıca bg2’de de bir sorun vardır: a otomatik bir dizidir; temel adresi yürütme esnasında belirlenir, oysa (ag’den farklı olarak) bg2’nin ilklemesi derleme esnasında yapılır, ancak bu durumda bu olanaksızdır.

Dizilerle göstergeler arasında ilgi çekici bir karşılaştırma aşağıda görülebilir:

char const msj1[] = "Sonraki lutfen? ";
char const *msj2  = "Sonraki lutfen? ";

Hem msj1 hem de msj2 karakter göstergeleri olarak değerlendirilebilirler ve birçok bağlamda birbirinin yerine kullanılabilirler. Fakat derleyici açısından bir fark söz konusudur. İlk tanımda, dizi için 17 bayt kullanılır, bu da msj1 dizisi için ayrılan bellek miktarıdır. İkincisinde ise, dizinin derleyici tarafından başka bir yerde saklanması (yine 17 bayt) ve bunun başlangıç adresinin msj2 için ayrılan yere (64 bitlik sistemlerde 8 bayt) depolanması sağlanır. Yani ikinci tanım daha fazla yer kaplar. Bu değişken bir gösterge için ödememiz gereken bedeldir; oysa programın yürütülmesi esnasında belki de bu göstergenin değerini değiştirmeyeceğiz. Bu tür program değişmezleri için ilk seçeneği (yani değişmez bir göstergeyi) kullanmamız daha akıllıca olacaktır.