14 Kasım 2020 Cumartesi

Belirleyiciler c programlama

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