16 Kasım 2020 Pazartesi

Struct Yapılar

 Yapılar (structures) da diziler gibi birden fazla nesneyi içlerinde tutabilen bir veri türürdür.

Yapıların da elemanları bellekte ardışıl (contigious) bir biçimde bulunur. Fakat yapıların
dizilerden temel farkı şudur: Diziler aynı türden nesneleri içinde tutabilirken, yapılar farklı
türlerden nesneleri tutabilirler. Yapıların kullanılmasının ana nedeni budur. Çoğu zaman, türleri
farklı bir takım nesneler, mantıksal olarak bir bütün oluşturabilirler. İsim, yaş, departman,
ücret, öğrenim durumu gibi bilgileri farklı türden nesneler içinde saklayabiliriz ve bunların
tamamı çalışan bir personele ait bilgiler olabilir. Bu gibi aynı konu ile ilgili farklı türden veriler
yapılar içinde saklanır.
Yapı Bildiriminin Genel Şekli
<struct > [yapı ismi] {
<tür> <m1>;
<tür> <m2>;
<tür> <m3>;
...
};
Yukarıdaki gösterimde struct bir anahtar sözcüktür. Bildirimde mutlaka yer alması
gerekmektedir. Yapı ismi (structure tag) C dilinin isimlendirme kurallarına uygun olarak
seçilmiş bir isimdir.
Örnek bildirimler:
struct SAMPLE {
int a;
long b;
char ch;
};
struct DATE {
int day, month, year;
};
struct POINT {
int x, y;
};
Yapı isimleri (structure tags) geleneksel olarak büyük harfle yazılır. Bu bir zorunluluk değildir.
Yapı bildiriminin yapılması bellekte derleyici tarafından bir yer ayrılmasına neden olmaz. Yani
bir tanımlama (definition) söz konusu değildir. Bu bildirimle (declaration) programcının
yarattığı yeni bir veri türü hakkında derleyiciye bilgi verilmektedir.
Yapı bildiriminin yapılmasından sonra artık bildirimi yapılmış yapı türünden nesneler
tanımlanabilir. Yapı bilidiriminin yapılması ile yeni bir veri türü yaratılmıştır. Derleyici artık bu
tür hakkında bilgi sahibi oldugundan, bu yeni veri türünden, nesneler tanımlanabilir. Yeni veri
türünden nesnelerin tanımlanması durumunda artık derleyici bu nesneler için bellekte ne kadar
bir alan ayırması gerektiği bilgisine sahip olacaktır.
Yapı Türünden Nesne Tanımlaması
<struct> <yapı isimi> <nesne ismi>;
Örnekler :
struct DATE x; /* x DATE yapısı türünden bir nesnedir. */
struct POINT p1, p1; /* p1 ve p2 POINT yapısı türünden nesnelerdir. */
struct SAMPLE sample; /* sample SAMPLE yapı türünden bir nesnedir. */
Yapı değişkenleri bileşik nesnelerdir. Yani parçalardan oluşurlar. Zaten yapı bildirimlerinin
yapılmasının amacı da bu parçaların isimleri ve türleri hakkında derleyiciye bilgi vermektir. Bir
yapı bildirimini gören derleyici, daha sonra bildirimi yapılan yapı türünden bir değişken
tanımlanması durumunda, bu değişken için bellekte ne kadar yer ayıracağını, ve ayırdığı yeri
ne şekilde organize edeceğini bilir.
Bir yapı değişkeni (nesnesi) için, yapı bildiriminde belirtilen elemanların toplam uzunluğu kadar
(byte olarak) yer ayrılır.
struct SAMPLE {
int a;
long b;
char ch;
};
void main()
{
struct SAMPLE x;
printf("%d\n", sizeof(x));
}
Yukarıdaki program parçasında ekrana (DOS altında) 7 sayısı yazdırılacaktır. Çünkü yapı
nesnesi olan x nesnesinin bellekte kapladığı yer üç parçasının kapladığı uzunluğun toplamıdır.
Bunu aşağıdaki şekilde de ifade edebiliriz:
sizeof(x) == sizeof(int) + sizeof(long) + sizeof(char)
(Hizalama [alignment] konusuna geldiğimizde bu durumu daha detaylı olarak inceleyeceğiz.)
Yapı Elemanlarına Erişim
Yapıların dizilerden önemli bir farkı da elemanlara erişim konusundadır. Dizi elemanlarına
erişim, dizi ismi (aslında bir adres bilgisidir) ve indis operatörü [ ] (index operator - subscript
operator) yoluyla yapılırken, yapı elemanlarına erişim doğrudan yapı nesnesinin ve elemanın
isimleriyle olur. Yapı elemanlarına erişimde nokta operatörü kullanılır.
Nokta operatörü (.) iki operand alan araek konumunda (binary infix) bir operatördür. Bu
operatörün sol tarafındaki operand bir yapı türünden değişken olmalıdır. Sağ tarafındaki
operand ise ilgili yapı türününün (yani yapı bildiriminde önceden belirlenmiş) bir üyesi olmak
zorundadır. Nokta operatörü, operatör öncelik tablosunun en yüksek düzeyinde bulunur.
Yapı nesneleri de yerel ya da global olabilir. Diğer nesnelerde olduğu gibi yapı nesneleri içinde
faaliyet alanı (scope) kavramı söz konusudur. Tüm blokların dışında tanımlanan yapı nesneleri
global iken blokların içlerinde tanımlanan yapı değişkenleri yereldir. Global yapı nesneleri diğer
türden global nesneler gibi statik ömür karakterine sahiptir ve dosya faaliyet alanı kuralına
uyarlar. Yerel yapı nesneleri ise dinamik ömürlüdür ve blok faaliyet alanı kuralına uyarlar.
Yapı değişkenlerine programcı tarafından değer verilmemişse, yapı değişkeni yerel (local) ise
tüm elemanlarında rasgele değerler (garbage value) bulunur. Yapı nesnesi global ise tüm
elemanlarında 0 değeri bulunur.
Bir yapı nesnesi tanımlanarak, bu yapı nesnesinin elemanlarına nokta operatörü ile
ulaşıldığında artık bu elemanların her biri ayrı bir nesne olarak ele alınır. Bu nesnelerin yapı
dışında tanımlanan nesnelerden herhangi bir farkı yoktur, nesnelerin tüm özelliklerine
sahiptirler.
Örnek:
struct SAMPLE {
int a;
long b;
char c;
};
int main()
{
struct SAMPLE x;
x.a = 10;
x.b = 200000;
x.c = 'M';
printf("x.a = %d\nx.b = %ld\nx.c = %c\n", x.a, x.b, x.c);
return 0;
}
Yukarıdaki örnekte görüldüğü gibi x.a, x.b ve x.c yapı elemanları ayrı birer nesne özelliği
gösterirler. Bu elemanlara ayrı ayrı ulaşabilir ve ayrı ayrı atamalar yapabiliriz. Bu nesneleri ++
ve -- operatörlerine ya da & operatörüne operand yapabiliriz.
Yapı Bildirimlerinin Yapılış Yerleri
Yapı bildirimi global ya da yerel olarak yapılabilir. Eğer yerel olarak yapılırsa yalnızca bildirimin
yapıldığı blokta o yapı türünden nesne tanımlanabilir. Bildirim global olarak yapılırsa her yerde
o yapı türünden değişken tanımlanabilir.
Uygulamalarda hemen hemen her zaman yapı bildirimleri programın tepesinde global olarak
yapılır. (Yapı bildirimi başlık dosyalarının içinde de yapılabilir. Bu durumu ileride detaylı olarak
inceleyeceğiz.)
Yapı bildiriminde yer alan yapı elemanlarının faaliyet alanı yapı bildirim bloğuyla sınırlıdır. Yani
yapı bildirimi bloğu dışında, yapı elemanı ile aynı isimli bir değişken tanımlanabilir. Yapı ismi
(structure tag) ile aynı isimli değişkenler de tanımlanabilir.
Aşağıdaki program parçasında isimlendirme açısından bir hata yok. Okunabilirlik açısından iyi
bir uygulama değil.
struct SAMPLE {
int sample;
long x;
};
int main()
{
int sample, SAMPLE;
...
return 0;
}
Yapı Elemanlarının Bellekteki Yerleşimi
Yapı elemanları belleğe, bildirimde ilk yazılan eleman küçük adreste olacak biçimde, ardışıl
olarak yerleştirilir.
Örnek:
struct SAMPLE {
int a;
long b;
char c;
};
int main()
{
struct SAMPLE x;
printf("x.a adresi = %p\nx.b adresi = %p\n x.c adresi = %p\n", &x.a, &x.b, &x.c);
}
Yapı Değişkenlerine İlk Değer Verilmesi (initialization)
Yapı değişkenlerine küme parantezleri içerisinde ilk değer verilebilir. Bu durumda verilen ilk
değerler sırası ile yapı elemanlarına yerleştirilir. Daha az sayıda yapı elemanına ilk değer
verilebilir, bu durumda ilk değer verilmemiş yapı elemanları 0 değeri alırlar.
struct DATE {
int day, month, year;
};
int main()
{
struct DATE x = {10, 12, 1999};
struct DATE y = {10, 12};
printf("%d %d %d\n", x.day, x.month, x.year};
printf("%d %d %d\n", y.day, y.month, y.year};
}
Dizilerde olduğu gibi, yapılarda da yapı elemanlarından daha fazla sayıda elemana ilk değer
vermek derleme zamanında hata oluşumuna neden olacaktır.
Yapı Elemanı Olarak Göstericilerin Kullanılması
Yapının bir elemanı herhangi türden bir gösterici olabilir. Bu durumda bu yapı elemanına
ulaşıldıdığında, bu gösterici de yapı elemanı olmayan göstericiler gibi değerlendirilir
Örnek:
struct PERSON {
char *name;
int no;
};
int main()
{
struct PERSON x;
x.name = "sifirzero";
x.no = 125;
printf("%s %d\n", x.name, x.no);
return 0;
}
Yukarıdaki örnekte yapı elemanlarına ilk değer verme sentaksıyla da (initialization) değer
atanabilirdi:
....
struct PERSON x = {"sifirzero", 125};
...

Hiç yorum yok:

Yorum Gönder

Her yorum bilgidir. Araştırmaya devam...