|
C' de Birliklerin (Unions) Kullanılması |
|
Gönderiliyor lütfen bekleyin... |
|
|
Birlikler, genel haliyle yapılara benzeyen, ancak elemanlarının bellekteki
yerleşimi açısından yapılardan farklılık gösteren kullanıcı tanımlı veri
türleridir. Yapılara göre
daha az kullanılırlar. Ancak uygulamada, birlik kullanarak programcının bir çok
avantaj sağlayacağı bazı durumlar vardır. Önce birliklerin yapılarla ortak olan
özelliklerini inceleyelim : Birlikler de yapılar gibi üye veri elemanlarına
sahiptirler. Bu veri elemanları C dilinin doğal türleri tipinde veya kullanıcı
tanımlı türleri tipinde olabilir. Hatta bir birliğin elemanı başka bir birlik
olabilir. Birlik nesnesinin elemanlarına nokta (.) operatörü ile erişilir. Eğer
birlik türünden bir gösterici kullanılıyorsa birliğin elemanlarına ok (->)
operatörü ile erişilir. Derleyici birlik veya yapı bildirimi gördüğü zaman
bellekte herhangi bir yer ayırma işlemi yapmaz. Yer ayırma işlemi, sadece o
türden bir nesne tanımlandığı zaman gerçekleştirilir. Dilerseniz buraya kadar
anlattıklarımı aşağıdaki kod örneği ile inceleyelim;
typedef union _test
{
int i;
double d;
}test;
// Buraya kadar henüz bellekte yer ayırma işlemi yapılmadı.
// ...
test t; // birlik türünden nesne tanımlanması (bellekte yer ayırma işlemi
yapıldı).
t.i = 4; // birlik elemanlarına nokta operatörü ile erişilmesi ve elemana değer
atanması.
t.d = 4.0;
test *pt; // birlik türünden gösterici tanımlanması.
pt->i = 2; // birlik elemanlarına ok operatörü ile erişilmesi ve elemana değer
atanması.
pt->d = 2.0;
// ... |
Şimdi de birliklerin yapılardan farklılıklarını inceleyelim: Birliklerin
yapılarla farklılık gösterdiği nokta, veri elemanlarının bellekteki
yerleşimidir. Derleyici bir birlik nesnesi tanımlandığı zaman, bu nesne için
bellekte birliğin en uzun elemanı kadar yer ayırır. Bilindiği gibi yapı
nesnelerinin bellekte kapladığı yer (hizalama seçenekleri
de göz önünde bulundurularak) tüm yapı elemanlarının uzunluklarının toplamı
kadardır. Önce yapılardaki durumu aşağıdaki örnek kod ile incelemeye çalışalım.
struct _stest
{
int i;
double d;
}stest;
// ...
stest test;
printf("%d\n",sizeof(test)); //Ekrana (4 + 8) 12 yazacaktır. (Hizalama seçeneği
byte-alignment ise ve 32 bitlik sistemde)
// ... |
Şimdi de birliklerdeki durumu inceleyelim :
union _utest
{
int i;
double d;
}utest;
// ...
utest test;
printf("%d\n",sizeof(test)); //Ekrana 8 yazacaktır.
// ... |
Görüldüğü üzere derleyici bir birlik nesnesi için bellekte, birliğin en fazla
yer kaplayan elemanı kadar yer ayırmaktadır. Birliğin elemanlarına atanan
değerler birliğin bellekteki adresinden başlayarak yazılmaktadır. Yani birliğin
tüm veri elemanları aynı adresten başlayarak yazılır. Birliğin en uzun veri
elemanı kadar yer ayrıldığı için herhangi bir taşma hatası da olmamaktadır. Bu
durumda bir elemana değer atadığımızda diğer bir elemandaki değerin üzerine
yazılacağı için bu elemanın değeri de kaybedilmiş olur. Ancak bu durum, birliğin
dezavantajı olarak görülmemelidir. Çünkü birliklerin varlık nedeni de zaten bu
özellikleridir.
Birliklerin bu özelliklerinden nasıl bir avantaj sağlayabiliriz? Uygulamada
birkaç şekilde birlik veri türüne ihtiyaç duyulabilir. Şimdi bir örnek üzerinde
ilerleyerek bu durumlardan birisini inceleyelim. Bir fabrikada bir malzemenin iki
farklı işlemde kullanıldığını düşünelim. Diyelim ki A malzemesi, hem op1
işleminde hem de op2 işleminde kullanılıyor. Biz bu iki işleme ilişkin bilgileri
tutmak istiyoruz. op1 işleminde A malzemesinin nitel özellikleri, op2 işleminde
ise nicel özellikleri önem taşısın. Malzemenin bu özelliklerini iki yapı
bildiriminde toplayabiliriz. Sistemde cins, renk ve şekil özelliklerinin
tamsayılar ile ifade edildiğini düşünelim :
typedef struct _nitelOzellikler
{
int cins;
int renk;
int sekil;
}nitelOzellikler; // 12 byte
typedef struct _nicelOzellikler
{
double agirlik;
double yogunluk;
double miktar;
}nicelOzellikler; // 24 byte |
op1 işlemine ilişkin bilgileri tutarken malzemenin nicel özelliklerine
ihtiyacımız yoktur. Benzer şekilde op2 işlemine ilişkin bilgileri tutarken de
malzemenin nitel özelliklerine
ihtiyacımız yoktur. Bu durumda bu iki tür özelliği veri elemanı olarak alan bir
birlik bildirelim :
typedef union _ozellikler
{
nitelOzellikler nitel;
nicelOzellikler nicel;
}ozellikler; // 24 byte |
Görüldüğü gibi birlik, bellekte en uzun veri elemanı kadar, yani 24 byte yer
kaplayacaktır. Bu özelliklerin haricinde malzemenin bir de genel özellikleri
olsun, bu özellikler hem op1 işleminde hem de op2 işleminde girdi olarak
alınsın. Genel özellikleri malzeme yapısının içerisinde bildirebiliriz. Malzeme
yapısında yukarıda bildirimini yaptığımız birlik türünden bir veri elemanını da
ekleriz. Yapılan işleme göre birliğin ilgili elemanını kullanırız.
typedef struct _malzeme
{
char *firmaAdi;
char *markasi;
double stokMiktari;
ozellikler ozellik;
}malzeme; //40 byte |
Bu bildirim ile, op1 işlemine ait bir işlemin bilgilerini tutarken gereksiz yere
malzemenin nicel özellikleri için bellek kullanılmayacaktır. op2 işlemi için de
gereksiz yere
malzemenin nitel özellikleri için bellek kullanılmayacaktır. Bu durumda
bellekten, op1 işlemi için 24 byte, op2 işlemi için ise 12 byte kazanç
sağlanacaktır. Malzeme yapısı türünden bir nesne bu haliyle bellekte 40 byte yer
kaplayacaktır. Eğer birlik kullanmayıp da tüm özellikleri aynı yapı nesnesi
içerisinde bildirmiş olsaydık bu yapı türünden bir nesne bellekte 52 byte yer
kaplayacaktır. Bu yapı nesnesi içerisindeki bilgileri op1 ve op2 işlemleri için
ekranda gösterecek iki fonksiyon yazalım :
void DisplayOp1(const malzeme *malz)
{
printf("%s\n",malz->firmaAdi);
printf("%s\n",malz->markasi);
printf("%lf\n",malz->stokMiktari);
printf("%d\n",malz->ozellik.nitel.cins);
printf("%d\n",malz->ozellik.nitel.renk);
printf("%d\n",malz->ozellik.nitel.sekil);
}
void DisplayOp2(const malzeme *malz)
{
printf("%s\n",malz->firmaAdi);
printf("%s\n",malz->markasi);
printf("%lf\n",malz->stokMiktari);
printf("%lf\n",malz->ozellik.nicel.agirlik);
printf("%lf\n",malz->ozellik.nicel.miktar);
printf("%lf\n",malz->ozellik.nicel.yogunluk);
} |
Şimdi bildirdiğimiz yapıyı ve fonksiyonları kullanarak ana bloğu oluşturalım
:
int main()
{
malzeme m1;
malzeme m2;
//op1 işlemi yapılıyor...
m1.firmaAdi = "fabrika1";
m1.markasi = "marka1";
m1.stokMiktari = 10.0;
m1.ozellik.nitel.cins = 1;
m1.ozellik.nitel.renk = 2;
m1.ozellik.nitel.sekil = 3;
//op2 işlemi yapılıyor...
m2.firmaAdi = "fabrika2";
m2.markasi = "marka2";
m2.stokMiktari = 20.0;
m2.ozellik.nicel.agirlik = 10.5;
m2.ozellik.nicel.miktar = 12.5;
m2.ozellik.nicel.yogunluk = 0.02;
DisplayOp1(&m1);
DisplayOp2(&m2);
return 0;
} |
Birliklerin bellek kazancı açısından yararını bu örnekle açıklamış olduk. Geniş
çaplı bir
uygulamada yer alabilecek daha karmaşık yapılar düşünüldüğünde ve özellikle
bellek
kullanımının kritik bir rol oynadığı uygulamalar göz önünde bulundurulduğunda
birlikler
kullanılarak ciddi bellek kazançları sağlanabilir. Bellek kazancının yanı sıra
kodun
okunabilirliği de birlik kullanımı sayesinde artmaktadır. Bir sonraki yazımızda
görüşmek dileğiyle hoşçakalın.
Kaynak kod için tıklayın.
Makale:
C' de Birliklerin (Unions) Kullanılması C ve Sistem Programlama Çiğdem Çavdaroğlu
|
|
|
-
-
Eklenen Son 10
-
Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor
|
|