BELİRLEYİCİLER
Belirleyiciler (specifiers), bildirimler yapılırken kullanılan ve nesnelerin ikincil özellikleri
hakkında derleyicilere bilgi veren anahtar sözcüklerdir. Belirleyicileri yer belirleyicileri (storage
class specifiers) ve tür belirleyicileri (type qualifiers) olarak iki grupta inceleyeceğiz.
Yer belirleyicileri genel olarak nesnelerin tutuldukları yerler hakkında bilgi verirler. Tür
belirleyicileri ise nesnelerin içindeki değerlerin değiştirilip değiştirilmeyeceğine ilişkin bilgi
verirler. C'de 4 tanesi yer belirleyici (auto, register, static, extern) ve 2 tane tür belirleyici
(const, volatile) vardır. Bu belirleyici isimleri C dilinin birer anahtar sözcüğüdür.
Yer ve Tür Belirleyicileriyle Bildirim İşlemi
Bildirimin genel biçimi:
[yer belirleyici] [tür belirleyici] <tür> nesne1, [nesne2], [nesne3], ...
auto const
register volatile
static
extern
Yer belirleyici, tür belirleyici ya da tür ifade eden anahtar sözcüklerin dizilimi herhangi bir
biçimde olabilir.
auto const unsigned long int a;
const auto unsigned long int a;
unsigned long int auto const a;
int const long auto unsigned a;
Yukarıdaki bildirimlerin hepsi geçerli bildirimlerdir. Ancak okunabilirlik açısından bildirimlerin
yukarıdaki genel biçime uygun olarak yapılması tavsiye edilir.
Eğer bir bildirimde yer ya da tür belirleyicileri bir tür bilgisi olmadan kullanılırsa, önceden
seçilmiş (default) olarak int türü bildirimin yapıldığı kabul edilir.
const a;/* const int a; bildirimi ile eşdeğerdir. */
Şimdi yer ve tür belirleyicilerini tek tek detaylı olarak inceleyelim :
auto Belirleyicisi
auto yalnızca yerel değişkenler için kullanılabilecek bir yer belirleyicisidir. auto belirleyicisinin global değişkenlerin ya da fonksiyonların parametre değişkenlerininin bildiriminde kullanılması derleme zamanında hata oluşturur. Bu anahtar sözcük, nesnenin faaliyet alanı bittikten sonra kaybolacağını, bellekte kapladığı yerin geçerliliği kalmayacağını gösterir. Yerel değişkenler bulundukları blok icra edilmeye başlandığında yaratılıyorlar, söz konusu bloğun icrası bittikten sonra yok oluyorlardı. İşte auto belirleyicisi bu durumu vurgulamak için kullanılmaktadır. Zaten bir yerel değişken, başka bir yer belirleyici anahtar sözcük kullanılmadığı sürece (default olarak) auto biçiminde ele alınır. Bu durumda auto yer belirleyicisinin kullanımı gereksizdir. { auto int a; float b; } auto yer belirleyicisi global değişkenlerle ya da parametre değişkenleriyle birlikte kullanılmaz. Örneğin :auto int a; /* hata çünkü global bir değişken auto olarak bildirilemez */ function(auto int x) /* hata parametre değişkeni auto biçiminde bildirilemez */ { auto int var;/* yereldeğişken auto olabilir */ int x; /*yerel değişken auto belirleyicisi kullanılmasa da auto olarak ele alınır*/ ... } auto anahtar sözcüğü bazı mikroişlemcilerde uyumu korumak için düşünülmüştür. Modern sistemlerde anlamlı bir kullanımı yoktur. register belirleyicisi register belirleyicisi, değişkenin “bellekte değil de CPU yazmaçlarının içerisinde” tutulacağını belirten bir anahtar sözcüktür. Değişkenlerin bellek yerine yazmaçlar içerisinde tutulması programın çalışmasını hızlandırır. Yazmaç (register) nedir? Yazmaçlar CPU (central processing unit) içerisinde bulunan tampon bellek bölgeleridir. CPU içerisindeki aritmetik ve mantıksal işlemleri yapan birimin yazmaçlar ve belleklerle ilişkisi vardır. Genel olarak CPU tarafından yapılan aritmetik ve mantıksal işlemlerin her iki operandı da belleğe ilişkin olamaz. Örneğin bellekte bulunan sayi1 ve sayi2 ile gösterdiğimiz 2 sayiyi toplayarak sayi3 ile gösterdiğimiz başka bir bellek bölgesine yazmak isteyelim. Bu C’deki sayi3 = sayi1 + sayi2; işlemine karşılık gelmektedir. CPU bu işlemi ancak 3 adımda gerçekleştirebilir: 1. adım : Önce sayi1 bellekten CPU yazmaçlarından birine çekilir MOV reg, sayi1 2. adım : Yazmaç ile sayi2 toplanır. ADD reg, sayi2 3. adım: Toplam data3 ile belirtilen bellek alanına yazılır. MOV sayi3, reg Belleğe yazma ve bellekten okuma işlemleri yazmaçlara yazma ve yazmaçlardan okuma işlemlerine göre daha yavaştır. Çünkü belleğe erişim için bir makine zamanı gerekmektedir. CPU yazmaçları hangi sistem söz konusu olursa olsun sınırlı sayıdadır. Bu nedenle birkaç değişkenden fazlası register belirleyicisi ile tanımlanmış olsa bile yazmaçlarda saklanmayabilir. C derleyicileri yazmaçlarda saklayamayacakları değişkenler için genel olarak hata veya uyarı mesajları vermezler. Yani derleyiciler, tutabilecekleri yazmaç sayısından fazla register belirleyicisine sahip değişkenlerle karşılaştıklarında bunlara ilişkin register belirleyicilerini dikkate almazlar. register belirleyicileri ancak yerel ya da parametre değişkenleri ile kullanılabilir global değişkenler ile kullanılamazlar. Örnekler register int x; /* hata x değişkeni global register belirleyicisi ile kullanılamaz */ int sample (register int y) /* hata değil */ { register float x; /* hata değil */ ... } Ne kadar değişkenin yazmaçlarda saklanabileceği bilgisayar donanımlarına ve derleyicilere bağlıdır. Ayrıca, uzunluğu tamsayı (int) türünden büyük olan türler genellikle yazmaçlarda saklanamazlar bu durumlarda da derleyicilerden hata veya uyarı mesajı beklenmemelidir. Sonuç olarak register belirleyicisi hızın önemli olduğu çok özel ve kısa kodlarda ancak birkaç değişken için kullanılmalıdır. Modern derleyicilerin çoğu (seçime bağlı) kod optimizasyonu yaparak bazı değişkenleri yazmaçlarda saklayabilirler. Bu durum da çoğu zaman register anahtar sözcüğünün kullanılmasını gereksiz kılar.
static Belirleyicisi
static belirleyicisine sahip değişkenler programın çalışma süresince bellekten kaybolmazlar. Bir bakıma static belirleyicisi auto belirleyicisinin zıt anlamlısıdır. static belirleyicisi ancak yerel ya da global değişkenlere birlikte kullanılabilirler. static belirleyicisi parametre değişkenleriyle kullanılmazlar. static anahtar sözcüğünün global ve yerel değişkenlerle birlikte kullanılması farklı anlamlara gelir bu yüzden bu durumları ayrı ayrı inceleyeceğiz: static Anahtar Sözcüğünün Yerel Değişkenlerle Kullanılması static yer belirleyicisine sahip olan yerel değişkenler programın icrası boyunca bellekte kalırlar. Başka bir deyişle, static anahtar sözcüğü yerel değişkenlerin ömrünü uzatmaktadır. Statik yerel değişkenler tıpkı global değişkenler gibi programın çalışmaya başlamasıyla yaratılırlar ve programın icrası bitene kadar da bellekte tutulurlar. Statik yerel değişkenler programcı tarafından ilk değer verildikten sonra kullanılırlar. İlk değer verme işlemi programın çalışması sırasında değil, derleme zamanında derleyici tarafından yapılır. (Derleyici bellekten yer ayrılmasına yol açacak makine kodunu oluşturur ve statik değişken için bellekte yer programın yüklenmesi sırasında ayrılır. "Derleyici tarafından bellekte yer ayrılır." derken bunu kastediyoruz. Derleme zamanında gerçekten bellekte yer ayrılmıyor, bellekte yer ayırma işini yapacak makine kodu yaratılıyor.) Statik yerel değişkenler ilk değerleriyle birlikte belleğe yüklenirler. Örnek : int statfunc(void) { static int x = 12; /* bu kısım yalnızca derleme sırasında ve bir kez işlem görür */ ++x; /* fonksiyonun her çağırılışında yeniden yaratılmaz ve değerini korur */ return x; } void main() { printf(“a = %d\n”, statfunc()); /* a = 13 */ printf(“a = %d\n”, statfunc()); /* a = 14 */ } Yukarıda verilen örnekte statfunc() fonksiyonunun içerisindeki yerel x değişkeninin değeri fonksiyonun her çağırılışında bir artırılır. Çünkü x statik yerel değişken olduğu için, fonksiyonun her çağırılışında yeniden yaratılmayacak, en son aldığı değeri koruyacaktır. Daha önce söylendiği gibi, bir adres değerine geri dönen fonksiyonlar yerel değişkenlerin adreslerine geri dönmemelidir. Ancak yerel değişkenin statik olarak tanımlanması durumunda, statik bir yerel değişken programın sonlanmasına kadar bellekteki yerini koruyacağından, fonksiyonun statik bir yerel değişkenin adresine geri dönmesinde bir sakınca yoktur: char * funk(void) { static char ch; ... return &ch; } Diziler bir ya da daha fazla nesnenin bir arada tanımlandığı veri yapıları olduguna göre, yerel diziler de static anahtar sözcüğüyle tanımlanabilirler. func(void) { static aylar[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; ... } Yukarıdaki örnekte, func() fonksiyonu içinde aylar dizisi static anahtar sözcüğüyle tanımlandığı için, bir kez ilk değer verildikten sonra her çağırıldığı zaman bu değerleri koruyacak ve varlığını programın sonlanmasına kadar devam ettirecektir. Oysa static anahtar sözcüğü kullanılmasaydı, bu dizi func() fonksiyonun her çağırılmasıyla yeniden yaratılacak ve tüm ilk değer atamaları her defasında yeniden yapılacaktı. Bu da fonksiyonun her çağırılmasında belirli bir makine zamanının tekrar harcanmasına neden olacaktı. extern Belirleyicisi ve Bağlantı Kavramı (Linkage) extern belirleyicisini açıklamadan önce linkage (bağlantı) kavramı üzerinde durmak istiyoruz. İlk derslerimizde nesnelerin özelliklerinden birinin de bağlantı (linkage) özelliği olduğunu söylemiştik. C standartları üç ayrı bağlantı sınıfı tanımlamıştır : 1. Dış Bağlantı (external linkage) Bir modülde tanımlanan bir nesneye programı oluşturan başka modüllerde de ulaşılabilmesi özelliğidir. Eğer bir değişkene başka bir modülde de tanınabiliyorsa o değişkenin dış bağlantısı var denir. 2. iç bağlantı (internal linkage) Bir modülde tanımlanan bir nesneye yalnızca kendi modülü içerisinde ulaşılabilmesi özelliğidir. Eğer bir değişken ancak kendi modülü içerisinde tanınabiliyorsa, diğer modüllerde tanınamıyorsa, o değişkenin iç bağlantısı var denir. 3. bağlantısız (no linkage) Bir modülde tanımlanan bir nesneye, o modül içinde yalnızca belirli bir blok içinde ulaşılabilmesi özelliğidir. Eğer bir değişken ancak kendi modülü içerisinde belirli bir blok dahilinde tanınabiliyor, bu blok dışında kendi modülü içinde tanınmıyorsa, o değişkenin bağlantısı yoktur denir. Global değişkenlerin dış bağlantısı varken, statik global değişkenlerin iç bağlantısı vardır. Yerel değişkenlerin bağlantısı yoktur. Bütün belirleyiciler içinde belki de anlaşılması en güç olanı extern belirleyicisidir. extern belirleyicisi genel olarak, derleyiciye nesnenin başka bir modülde tanımlandığını bildirmek için kullanılır.
const belirleyicisi
const ilk değer atandıktan sonra nesnenin içeriğinin değiştirilemeyeceğini anlatan tür belirleyici, bir anahtar sözcüktür. Yerel, global ve parametre değişkenleriyle birlikte kullanılabilir. Örneğin : const double PI = 3.14159265 /*Geçerli */ main() { const int i = 10; i = 100; /* hata */ } Bir değişken const belirleyicisi ile tanımlanacaksa ilk değer verilmelidir. Aksi halde const belirleyicisi kullanmanın bir anlamı kalmaz. Aşağıdaki örneği inceleyiniz. void sample (void) { const int a; /* anlamsız */ } Bu örnekte a yerel bir değişkendir, dolayısıyla rastgele bir değere sahiptir. İçeriğini bir daha değiştiremeyeceğimize göre tanımlanmasının da bir anlamı olamaz. const belirleyicisinin kullanım amacı ne olabilir diye düşünebilirsiniz? Sıklıkla şu merak edilir: “Eğer const belirleyicisi koruma amaçlı olarak kullanılıyorsa kim kime karşı korunuyor? const belirleyicisinin iki yararlı işlevi vardır.: Okunabilirliği artırır. Çünkü programı inceleyen bir kişi const belirleyicisine sahip değişkenin değerinin bir daha değiştirilemeyeceğini düşünerek daha fazla bilgi edinir. Yanlışlıkla nesnenin değerinin değiştirilmesi engellenir. const belirleyicisi değeri hiç değişmeyecek sabitler için kullanılmalıdır. const bildirimlerinin nesne yarattığına nesnenin yalnızca okunabildiğine (read only) dikkat ediniz. volatile belirleyicisi Derleyiciler optimizasyon amacıyla nesneleri geçici olarak yazmaçlarda tutabilir. Yazmaçlardaki bu çeşit geçici barınmalar register belirleyicisi kullanılmasa da derleyiciler tarafından yapılabilir. Örneğin: int kare (int a) { int x; x = a * a; return x; } Yukarıdaki fonksiyonda x geçici bir değişkendir, dolayısıyla derleyici x değişkenini bellekte bir yerde saklayacağına, geçici olarak yazmaçlarından birinde saklasa da işlevsel bir farklılık ortaya çıkmaz. Bu çeşit uygulamalarda derleyicinin değişkenleri geçici olarak yazmaçlarda saklaması işlemleri hızlandırmaktadır. Aşağıdaki kodu inceleyiniz: int a; int b; ... a = b; if (a == b) { ... } Bu örnekte doğal olarak ilk adımda b değişkeni a değişkenine aktarılmak üzere yazmaçlardan birine çekilecektir. Ancak derleyici if içerisindeki ifadede a == b karşılaştırmasını yapmak için bellekteki b yerine yazmaçtaki b’yi kullanabilir. Verdiğimiz iki örnekte de derleyici birtakım optimizasyonlarla programı işlevi değişmeyecek biçimde daha hızlı çalışır hale getirmek istemiştir. Ancak kimi uygulamalarda derleyicinin bu biçimde davranması hatalara neden olabilmektedir.
Hiç yorum yok:
Yorum Gönder
Her yorum bilgidir. Araştırmaya devam...