BELİRLEYİCİLER (SPECIFIERS)

BELİRLEYİCİLER
(SPECIFIERS)

Belirleyiciler, bildirimler yapılırken kullanılan ve nesnelerin ikincil özellikleri hakkında derleyicilere bilgi veren anahtar sözcüklerdir.

Belirleyicileri iki grupta inceleyeceğiz :

yer belirleyicileri (storage class specifiers)
tür belirleyicileri (type specifiers)

Yer belirleyicileri genel olarak nesnelerin tutuldukları yerler hakkında bilgi verirler. Tür belirleyicileri ise nesnelerin içindeki değerlerin değiştirilmesine ilişkin bilgi verirler.
C’de 4 tanesi yer belirleyici (auto, register, static, extern) ve 2 tanesi ise tür belirleyici (const, volatile) olmak üzere toplam 6 tane belirleyici olarak kullanılan anahtar sözcük vardır.

yer ve tür belirleyicilerle bildirim işlemi

genel biçim:

[yer belirleyici] [tür belirleyici] 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;

geçerli bildirimlerdir. Hatta

int const long auto unsigned a;

da geçerli bir bildirim oluşturur.

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 belirleyici(ler)i bir tür bilgisi olmadan kullanılırsa 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ı çok özel durumların dışında tamamen 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, data2

2. toplam data3 ile belirtilen bellek alanına yazılır.
MOV data3, reg

Belleğe yazma ve bellekten okuma işlemleri yazmaçlara ve yazmaçlardan okuma işlemlerine göre daha yavaştır. Çünkü belleklere 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 ve uyarı mesajları vermezler.
Yani derleyicileri 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;
}

Ne kadar değişkenin yazmaçlarda saklanabileceği bilgisyar 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 saklnamazlar 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. register belirleyicisinin olur olmaz yerde bilinçsizce kullanılması çalışma hızının yavaşlamasına bile neden olabilir.
Ayrıca modern derleyicilerin çoğu (opsiyonel olarak) 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.

statik belirleyicisi

statik belirleyicisine sahip değişkenler programın çalışma süresince bellekten kaybolmazlar. Bir bakıma statik belirleyicisi auto belirleyicisinin zıt anlamlısıdır.
statik belirleyicisi ancak yerel ya da global değişkenlere birlikte kullanılabilirler. statik belirleyicisi parametre değişkenleriyle kullanılmazlar.
statik 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ı

statik 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. static 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. statik yerel değişkenler ilk değerleriyle birlikte belleğe yüklenirler. Örnek :

int statfonc(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 değerini korur */
return x;
}

void main(void)
{
int a;

a = statfonc();
printf(“a = %d\n”, a); /* a = 13 */
a = statfonc();
printf(“a = %d\n”, a); /* a = 14 */
}

Yukarıda verilen örnekte statfonc() fonksiyonunun içerisindeki yerel x değişkeninin değeri fonksiyonun her çağırılışında bir artırılır. Çünkü x static 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)
{
statik char ch;


return &ch;
}

Diziler bir ya da daha fazla nesnenin bir arada tanımlandığı veri yapıları olduguna göre, yerel diziler de statik anahtar sözcüğü le tanımlanabilirler:

funk(void)
{
static aylar[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

}

Yukarıdaki örnekte, funk 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 funk 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 makina zamanının tekrar harcanmasına neden olacaktı.

statik yerel değişkenler ile global değişkenlerin karşılaştırılması

statik yerel değişkenler ömür açısından global yerel değişkenler ile aynı özellikleri gösterirler. Yani programın çalışmaya başlaması ile yaratılırlar ve programın çalışması bitince ömürleri sona erer. Ama faaliyet alanı (scope) açısından farklılık gösterirler. Global değişkenler dosya faaliyet alanına uyarken statik yerel değişkenler blok faaliyet alanına uyarlar. statik yerel değişkenlerin faaliyet alanlarının dar olması (blok faaliyet alanına sahip olması ) soyutlamayı (abstraction) ve yeniden kullanılabilirliği (reusability) kuvvetlendirmektedir. Çünkü global değişkenlere bağlı olan fonksiyonlar projeden projeye taşınmazlar ve kendi içlerinde bir bütünlük oluşturmazlar.

static anahtar sözcüğünün global değişkenler ile kullanılması

statik belirleyicisi global bir değişken ile birlikte kullanılırsa, global değişkenin faaliyet alanını, yalnızca tanımlandığı modülü kapsayacak biçimde daraltır. Yani global değişkenleri kendi modülleri içine hapseder. static global değişkenler yalnızca tanımlandıkları modül içerisinde faaliyet gösterebilirler. static global değişkenleri daha iyi anlayabilmek için modül kavramının ne olduğunu bilmek zorundayız:

Bir proje birbirinden bağımsız olarak derlenebilen birden fazla kaynak dosyadan oluşabilir. Projelerin bağımsız olarak derlenebilen herbir kaynak dosyasına modül denir.

Bir projeyi oluşturan farklı kaynak dosyalar birbirinden bağımsız olarak derlendikten sonra , hepsi birlikte bağlayıcı (linker) ile birleştirilerek tek bir .exe dosyası oluşturulur.

Büyük projelerin modüllere ayrılmasının ne gibi faydaları vardır?
Eğer bütün modüller tek bir kaynak kod içerisinde birleştirilirse en ufak bir değişiklikte tüm proje tekrar derlenmek zorundadır. Oysa modüllere ayrılmış projelerde, yalnız değişikliğin yapıldığı modülün derlenmesi yeterlidir. Çünkü diğer modüller zaten derlenmiştir ve onlar yalnızca bağlama aşamasında işlem görürler. Modüller halinde yazmanın bir diğer avantajı da grup çalışması yaparken ortaya çıkar. Bu durumda projelerin bağımsız parçaları (modülleri) ayrı kişiler tarafından hazırlanabilir.
Global bir değişken normal olarak tüm modüller içerisinde faaliyet gösterebilirken (bu durumda bir sonraki konuda açıklayacağımız gibi diğer modüllerde extern bildiriminin yapılmış olması gerekmektedir.) static global değişkenler yalnızca tanımlandıkları modül içerisinde faaliyet gösterirler.
global değişkenler için static tanımlamasının yalnızca faaliyet alanı üzerinde etkili olduğuna, ömür üzerinde etkili olmadığına dikkat ediniz.

Farklı modüllerde aynı isimli iki static global değişken tanımlanabilir. “C’de aynı faaliyet alanına sahip aynı isimli birden fazla değişken tanımlanamaz” kuralını anımsayalım. İki farklı modülde tanımlanann aynı isimli iki statik global değişkenin faaliyet alanı farklıdır.

extern belirleyicisi ve linkage (bağlantı kavramı)

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, static 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

Bir proje MOD1.C ve MOD2.C biçiminde iki modülden oluşmuş olsun :

MOD1.C MOD2.C
int a;

float fonk1()
{

a = 100;

}

main()
{


} int fonk2()
{

a = 300; /* HATA */
}

MOD1.C modülünde tanımlanmış olan a değişkeni global bir değişkendir ve dosya faaliyet alanına uyduğu için normal olarak proje içindeki diğer modüllerde de faaliyet gösterebilir. MOD2.C modülünde de faaliyet gösterebilir. Fakat iki modülün ayrı ayrı derlendiği yukarıdaki örnekte problemli bir durum söz konusudur. Çünkü MOD2.C modülünün derlenmesi sırasında derleyici a değişkeninin MOD1.C modülü içerisinde global olarak tanımlandığını bilemez. MOD2.C modülünü derlerken a değişkeni hakkında bilgi bulamayan derleyici bu durumu hara (error) olarak belirler. (C dilinde bir değişken ancak önceden bildirilmişse kullanılabilir.)
extern belirleyicisi derleyiciye, ilgili global değişkeninin kendi modülü içerisinde değil de bir başka modül içerisinde tanımlı olduğunu bildirme amacıyla kullanılır.

MOD2.C modülündeki a değişkeninin extern olarak bildirilmesi durumunda söz konusu problem ortadan kalkar.

MOD2.C

extern int a; /* extern bildirimiyle a değişkeninin başka bir modülde tanımlanmış olduğu belirtiliyor */

int fonk2()
{

a = 300;

}
extern bildirimini gören derleyici değişkenin projeye ait başka bir modülde tanımlandığını varsayarak hata durumunu ortadan kaldırır. Ancak derleyici makine kodu üretirken extern olarak bildirilmiş bir değişkenin bellekteki yerini tespit edemeyeceğinden , bu işlemi bütün modülleri gözden geçirecek olan bağlayıcı programa bırakır. Böylece değişkenin tanımlandığı modülü bulup, extern olarak bildirilmiş olanlarla ilişkilendirme işlemi bağlayıcı (linker) tarafından yapılmaktadır. Yani extern belirleyicisi ile programcı derleyiciye, derleyici ise bağlayıcıya bildirimde bulunmaktadır.

extern belirleyicisini gören derleyici bellekte bir yer ayırmaz. extern bildirimi bir tanımlama değildir. Sadecebildirimdir.(declaration)

aslında yalnız değişkenler için değil fonskiyonlar için de extern belirleyicisinin kullanılması söz konusudur.
C derleyicileri kendi modülleri içerisinde tanımlanmadıkları halde çağırılan (standart C fonksiyonları gibi) fonksiyonları otomatik olarak extern kabul ederler. Bu nedenle fonksiyon prototiplerinde ayrıca extern belirleyicisini yazmaya gerek yoktur. Çünkü derleyici tarafından yazılmış varsayılır.

Örneğin yukarıda verilen örnekte MOD2.c modülünde bulunan y1 fonksiyonu içerisinde bulunan x1 fonksiyonu çağırılıyor olsun:

MOD1.c MOD2.c
int a;

float x1()
{

a = 100;

}

main()
{

} extern int a;
extern float x1(void);

int y1()
{
float f;

f = x1();
a = 300;

}

MOD2.c modülünde x1 fonksiyonu için yazılmış olan prototip ifadesini inceleyiniz:

extern float x1(void);

Bu örnekte x1 fonksiyonu başka bir modülde tanımlı olduğu için prototip ifadesine extern belirleyicisi konmuştur. Ancak konulmasaydı derleyici zaten extern varsayacaktı. (tıpkı yerel değişkenler için auto belirleyicisini varsaydığı gibi)

Bir global değişken hiçbir modülde tanımlanmadan, bütün modüllerde extern olarak bildirilirse, tüm modüller hatasız bir şekilde derlenebilir. Hata bağlama aşamasında, bağlayıcının extern olarak bildirilen nesneyi hiçbir modülde bulamaması biçiminde ortaya çıkacaktır.

extern belirleyicisinin tek bir modül söz konusu olduğunda “amaç dışı” bir kullanımı vardır. Aşağıdaki örnekte main fonksiyonu içerisindeki global x değişkeni, tanımlanmadan önce kullanıldığından hataya neden olmaktadır.

void main()
{

x = 100;
}

int x; /* x global bir değişken ama tanımlanmasından önce yukarıda kullanılmış */

int fonk()
{
….
x = 200;

}

yukarıdaki kod derlendiğinde , main fonksiyonunun içerisindeki x değişkeninin bildiriminin bulunamadığını ifade eden bir hata mesajıyla karşılaşılır. Bu durumda, eğer bir global değişken tanımlamadan önce kullanılıyorsa, hata oluşmaması için daha önce extern bildiriminde bulunulmalıdır.

extern int x;

void main()
{

x = 100;

}

int x;

int fonk()
{
….
x = 200;

}

extern bildirimini bu şekilde kullanmak yerine, global değişkeni programın en başında tanımlamak daha iyi bir tekniktir.

extern bildirimlerinin yapılış yerleri

extern bildirimleri kaynak kodun herhangi bir yerinde yapılabilir. Global ya da yerel olması sözkonusu değilldir. Bildirimin yapıldığı yerden dosya sonuna kadarcolan bölge için geçerlidir. Ancak extern bildirimlerinin programın tepesinde ya da programcıya ait bir başlık dosyasının içinde yapılmasın ısalık veririz.

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(void)
{
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.:

1. 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.

2. 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.

NOT : C++’da const belirleyicisi zorunluluk olmadıkça nesne yaratmaz. (#define önişlemci komutu gibidir, ancak derleme aşamasında işlem görür)

const anahtar sözcüğünün gösterici tanımlamalarında kullanılması :

const anahtar sözcüğünün göstericilerle birlikte üç ayrı kullanılma biçimi vardır. Kullanılan her biçimde tanımlanan göstericinin özelliği değişecektir :

1. const anahtar sözcüğünün gösterici tanımlamasında ilk sözcük olarak kullanılması. Örnekler :

double dizi[20];
int k;
const char *str;
const int *ptr;
const double *dptr;

Bu durumda göstericinin gösterdiği nesne (yani gösterici içindeki adreste yer alan nesne) değiştirilemez ancak göstericinin içeriği değiştirilebilir. Bu tür bir tanımlamadan sonra göstericinin gösterdiği yerdeki nesnenin değiştirilmeye çalışılması derleme zamanı hatası ile neticelenecektir:

*str = ‘A’; /* error */
ptr[2] = 25; /* error */
*dptr = 4.5; /* error */
str = (char *)0x1FFF0; /* legal */
ptr = &k; /* legal */
dptr = dizi; /* legal */

const anahtar sözcüğünün göstericilerle birlikte kullanılmasında en sık görülen biçim budur ve özellikle fonksiyonların parametre değişkenleri olan göstericilerle bu biçim kullanılır:

void funk(const char *s)
{

}

yukarıda funk fonksiyonunun parametre değişkeni olan s göstericisi değerini fonksiyonun çağırmasıyla alacaktır. fonksiyon çağırma ifadesindeki arguman fonksiyonun açğırılmasıyla s göstericisine kopyalanacaktır. Ancak fonksiyon içinde *s nesnesine ya da s[x] nesnesine bir atama yapılamaz. Yani s göstericisinin gösterdiği yerdeki nesne (fonksiyona adresi gönderilen nesne) değiştirilemez.
Yukarıdaki örnekte const anahtar sözcüğünün kullanılması herşeyden önce okunabilirlik ile ilgilidir. Yukarıdaki kodu okuyan bir C programcısı funk fonksiyonunun dşarıdan adresi alınan nesnenin yalnızca değerinden istifade edeceğini, yani bu nesnenin değerini değiştirmeyeceğini anlar. Geleneksel olarak, const anahtar sözcüğünün fonksiyonun parametre değişlknei olan göstericilerin tanımlanmasın kuallanılmaması da okunabilirlik açısından bir mesaj olarak kabul edilir:

void funk(char *s)
{

}

yukarıdaki kodu okuyan bir C programcısı funk fonksiyonunun, aslında sentaksa açısından bir zorunluluk bulunmasa da) kendisine adresi gönderilen nesneyi değiştireceğini anlar. Kodu okuyan kişi şöyle düşünecektir : Eğer funk fonksiyonu adresi gönderilen nesneyi değiştirmeyecek olsaydı, s göstericisi const anahtar sözcüğü ile tanımlanırdı.

2. const anahtar sözcüğünün gösterici isminden hemen önce kullanılması . Örnekler :

char ch;
int m;
float f;
float fdizi[20];
char *const str = &ch ;
int *const ptr = (int *) 0x 1AC3;
float *const fptr = &f;

Bu durumda göstericinin gösterdiği yerdeki nesne değiştirilebilir ama göstericinin içeriği değiştirilemez:

str = (char *) 0x1FC0; /* error */
ptr = &m; /* error */
fptr = fdizi; /* error */

*str = ‘A’; /*legal */
*ptr = 15; /*legal */
fptr[2] = 12.3f; /*legal */

const anahtar sözcüğünün bu kullanım biçiminde gösterici tanımlanırken ilk değer verilmelidir, yoksa const anahtar sözcüğünün kullanılmasının bir anlamı kalmaz.

3. const anahtar sözcüğünün hem tanımlama ifadesinden önce hem de gösterici isminden önce kullanılması. Örnekler :

char ch;
int k;
const char *const str = (char *) 0x1AAA;
const char *const ptr = (int *) 0x1FFF;

Bu durumda ne göstericinin içeriği değiştirilebilir ne de göstericinin gösterdiği yerdeki nesne değiştirilebilir. her iki durum da derleme zamanında error oluşumuna yol açacaktır :

str = &ch; /* error */
*str = ‘A’; /* error */
ptr = &k; /* error */
*ptr = 12; /* error */

const sözcüğünün bu biçimde kullanılması yine daha çok parametre değişkenlerinin tanımlanmasında görülür ise de bu uygulamalarda seyrek olan bir durumdur.

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. İkinci örnekte :

a = b;

/* Bir kesme gelerek b’yi değiştirebilir! */

if (a == b) {

}

Bir donanım kesmesi (örneğin 8h gibi) b’yi değiştiriyorsa, bu durum if deyimi tarafından farkedilmeyebilir. İşte bu tür durumlarda değişkenlerin optimizasyon amacıyla geçici olarak yazmaçlarda tutulması arzu edilmeyen sonuçların oluşmasına yol açabilmektedir. volatile “ Değişkenleri optimizasyon amacıyla yazmaçlarda bekletmei, onları bellekteki gerçek yerlerinde kullan!” anlamına gelen bir tür belirleyicisidir. Bu anlamıyla volatile belirleyicisini register belirleyicisi ile zıt anlamlı olarak düşünebiliriz. Yukarıdaki örnekte b değişkenini volatile olarak bildirerek anlattığımız gibi bir problemin çıkması engellenebilir.

int a;
volatile int b;

volatile çok özel uygulamalarda kullanılabilen bir belirleyicidir.

Yerel, parametre ya da global değişkenlerle birlikte kullanılabilen volatile belirleyicisi ancak çok özel uygulamalarda önemli olabilmektedir. Bu belirleyicinin bilinçsizce kullanılmasının performansı kötü yönde etkileyebileceğini unutmayınız.

belirleyicilerin kullanılışlarını gösteren bir örnek :

#include

int a;
extern int b;
static int c;

void func(int d, register int e)
{
auto int g;
int h;
static int i;
extern int j;
register int k;
}

yukarıdaki programda tanımlanan değişkenlerin faaliyet alanı, ömür ve bağlantı açısından özellikleri aşağıdaki tabloda belirtilmektedir :

(isim)
name (ömür)
storage duration (faaliyet alanı)
scope (bağlantı)
linkage
a statik (static) dosya (file) dış (external)
b statik (static) dosya (file) dış (external) *
c statik (static) dosya (file) dış (external)
d otomatik (automatic) blok (block) yok (none)
e otomatik (automatic) blok (block) yok (none)
g otomatik (automatic) blok (block) yok (none)
h otomatik (automatic) blok (block) yok (none)
i statik (static) blok (block) yok (none)
j statik (static) blok (block) dış (external) *
k otomatik (automatic) blok (block) yok (none)

* b ve j değişkenleri kendi modülleri içinde tanımlanan global değişkenler de olabilir. Bu durumda bu değişkenle static anahtar sözcüğüyle tanımlanmış olmaları durumunda iç bağlantıya sahip olacak, static anahtar sözcüğü olmadan tanımlanmışlar ise dış bağlantıya sahip olacaklardır.

belirleyicilerin hangi tür değişkenlerle kullanılabileceklerini gösteren tablo :

belirleyici global
değişken yerel
değişken parametre değişkeni
auto 
static  
register   
extern  
volatile  
const   

Yorum yazın