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.