Temel C Programlama -44- Yapılar

Şimdiye kadar anlattığımız C konuları içinde veri yapıları olarak değişkenleri ve dizileri gördünüz. Değişkenler bağımsız ve bireysel veri tipleri iken diziler ise bu değişkenlerin ipe boncuğun dizilmesi gibi birbiri ardınca sıralanmasından oluşuyordu. En önemli nokta ise bir dizide bütün elemanlar aynı tip olmak zorundaydı. Örneğin hem karakter hem de tam sayı içeren bir dizi tanımlamanızın imkanı yoktu. Üstelik bu çok boyutlu diziler için de geçerliydi. Örneğin bir veri tipinde biz bir öğrencinin adını, okul numarasını ve notunu tutmak isteyebiliriz. Bunun için ad değeri karakter dizisi, numara tam sayı ve not da tam sayı olmak zorundadır. Bunu dizilerle yapmamız mümkün değildir. O halde daha karmaşık bir veri yapısına ihtiyacımız vardır. Bu veri yapısı içerisinde farklı tipte değişkenleri barındıracak ve bir arada bulunduracak bir veri yapısı olmalıdır.

Yapılar

İşte burada yapılar devreye girmektedir. Yapılar aslında nesne tabanlı programlamanın da temelini oluşturmaktadır. Çünkü yapılarla aslında bir fonksiyonsuz, işlevsiz ve sadece özelliklere sahip bir nesne tanımlarız. Bu veri yapısı bir bakımdan veri tabanlarındaki kayıtlara benzemektedir. Örneğin bir karton kutu yapısında en, boy ve yükseklik olmak üzere üç ayrı özelliğe ihtiyacımız vardır. Bu durumda int en, boy, yukseklik olarak üç ayrı değişkeni tanımlayabiliriz fakat normal değişken tanımlama ile bu değişkenler arasında bağ kuramayız. Ayrıca aynı tip özelliklere sahip fakat farklı üyeler tanımlamak istediğimizde bunlar için birbiriyle alakasız boy2, en2, yukseklik2 ve boy3, en3, yukseklik3 diye devam eden değişkenler tanımlamamız gerekir. Bu da programı işin içinden çıkılmaz hale sokacaktır. Yapılar bütün bu iki problemi de çözmekte ve daha karmaşık veri yapıları üzerinde çalışmamıza imkan tanımaktadır. Şimdi yapıların ne olduğuna derinlemesine göz atalım.

Yapılar birbiriyle alakalı değişkenlerin bir isim altında toplanmasıdır. Bu birbiriyle alakalı değişkenler dizilerde de olsa da diziler ancak tek bir tipte değer içerebilir ve karmaşık bir veri depolamaya imkan tanımaz. Yapılar ise farklı tipte değişkenleri bir arada barındırdığı gibi dizilerdeki ipe dizilen boncuklar gibi olan alaka yerine burada aynı kutuya konan parçalar gibi bir alaka sağlanmış olur. Umarım benzetmeden meseleyi anlamışsınızdır. Yapılar birbiriyle ilişkili değerleri bir çatıda topladığı gibi birden çok yapı da daha büyük bir yapı çatısı altında toplanabilir. Bu veri hiyerarşisi dizilerdeki çizgisel ilişkiden çok daha karmaşıktır.

Şimdi size bir yapının nasıl olabileceğini resmedelim. Öncelikle dizi elemanı oluşturalım ve buna değerleri verelim.

Burada varsaydığımız bir örnek dizide elemanların birbiriyle ilişkisi dizi erişim operatörünün değeri ve sıralamasından ibarettir. Bu diziler nicelik olarak çok olabilse de tek bir niteliği ifade etmektedir. Şimdi örnek bir yapının tablosuna bakalım

Gördüğünüz gibi kutu1 adlı yapıda en, boy ve yukseklik adlarında üç ayrı değer yer almakta ve her birinin hücresi birbirinden farklı olmakta. Burada dizi elemanları gibi sıra olmayıp farklı adlar da yer alabilmektedir. Ama hepsinin kutu1 çatısı içerisinde olduğunu unutmayın. Bu değişkenlere ancak kutu1 yapısına erişmekle erişme imkanımız olur. Aynı fonksiyonların içinde yer alan değerler gibi buradaki değişkenler kutu1 yapısının dışında tek başlarına erişim imkanı olmadığı gibi bir anlam da ifade etmemektedirler. Şimdi yukarıda tablosunu çizdiğimiz yapıyı C dili ile tanımlayalım.

struct kutu 
{
   int en;
   int boy;
   int yukseklik
}; // Noktalı virgülü unutma

Biz böyle diyerek sadece bir adet yapı değişkeni tanımlamadık. Bir yapı tipi tanımladık. Aynı int, char, float gibi birbirinden bağımsız değişken tipleri gibi bu yapı tipiyle de istediğimiz kadar birbirinden farklı yapı değişkeni tanımlayabiliriz. Burada bir nevi kendi değişken tipimizi oluşturduk diyebiliriz. Yalnız bu değişken tipi mevcut değişkenlerin bir araya getirilmesiyle daha karmaşık bir değişken olarak oluşturuldu. Şimdi kutu1 adında bir yapı değişkeni tanımlayabiliriz.

struct kutu kutu1;

Burada kutu adında bir yapı tipi tanımlasak da bunu değişken tanımlar gibi kutu kutu1; şeklinde tanımlama imkanımız burada yoktur. İleride tip tanımlayıcıları (typedef) konusuna geldiğimizde yapıların sadece adları ile de tanımlanabildiğini göreceğiz. İster struct kutu kutu1; diyin isterseniz de tip tanımlayıcıları ile kutu kutu1; yapın sonuç hep aynı olacaktır. Sadece biri daha kolaylaştırılmış şeklidir ve C++ dilindeki sınıf tanımlamalarına daha çok benzemektedir.

Yapılara değer atamak ve yapı erişim operatörü

C dilinde yapılar için kullanılan iki özel operatör bulunmaktadır. Biri ok operatörü olan (->) operatörüdür. Bu operatör yapı değerinin adresi (işaretçi) üzerinden değere erişme operatörü olup anlaması biraz karışıktır. Standart olan operatör ise nokta operatörü (.) olup doğrudan yapı değerine ulaşmaktadır. Şimdi bu operatörün nasıl kullanılıp değer atandığını size gösterelim.

struct kutu kutu1;
kutu1.en = 50;
kutu1.boy = 100;
kutu1.yukseklik = 150;

Gayet basit görünüyor değil mi. Biz struct kutu kutu1 diyerek kutu tipinde bir yapı değeri tanımladık. Kutu tipindeki yapıya baktığımızda içinde en, boy ve yukseklik olmak üzere üç adet int tipinde değer olduğunu görürüz. O halde kutu1.en dediğimizde kutu1 yapısının içindeki en değişkenine erişmiş oluruz ve = operatörü ile 50 değerini atarız. Eğer eşittirin sağ tarafına koysaydık yapı üyesinin içindeki değeri de getirme imkanımız olurdu. Bunlar üzerinde aritmetik işlem de yapabiliriz. Nokta operatörünü koyduktan sonra aynı değişkenler gibi çalışmaktadırlar. Bu değerlere erişme aynı değişken kapsamlarında olduğu gibi sınırlandırılmaktadır. Yani bir fonksiyonun içinde yapı tanımladıysak başka bir fonksiyonun içinde buna erişemeyiz. Ama yapı değişkenlerinin de aynı değişkenler gibi fonksiyonlara argüman olarak gönderilebileceğini unutmayalım.

Yukarıda tanımladığımız kutu1 adlı yapı değişkenine değerlerini üç ayrı satır kod yazarak verdik. Bunu her zaman böyle yazmak pek pratik olmayabilir. O yüzden dizilere değer atar gibi süslü parantezler içerisine de bu değerleri yazabiliriz. Böylelikle ilk tanımlamada değerlerini vermiş oluruz.

struct kutu kutu1 = { 50, 100, 150 };

Burada 50, 100, 150 diye yazdığımız değerler sırayla yapı değişkeninin en, boy ve yukseklik elemanlarına aktarılmaktadır. Burada yapı prototipini göz önünde bulundurmak ve yapı tanımlarken bunlara uygun değerleri atamak çok önemli. Elimizin altında basit bir değişken yok bir yapı var. Bu durumda daha dikkatli olmalıyız.

Yapı tanımlama ile alakalı bir özellik de yapı tipini tanımladığımız yerde yapı değişkenlerini tanımlama imkanımızdır. Böylelikle kullanılacak değişkenler derli toplu halde olmaktadır. Örneğin yukarıdaki kutu tipinde yapıda üç adet yapı değişkeni tanımlamak istersek şu şekilde yazabiliriz.

struct kutu 
{
   int en;
   int boy;
   int yukseklik
} kutu1, kutu2, kutu3;

Ayrıca yapılar dizi halinde de tanımlanabilir. Hem yapı hem de dizi olacak bir veri tipi size kafa karıştırıcı görünmüş olabilir. Ama daha veri yapılarına geçeceğiz! Bunlar size çocuk oyuncağı gibi gelmeli.

struct kutu kutular[20];

Burada kutu tipinde ve kutular adında toplam yirmi adet yapı değişkeni oluşturduk. Bu yapı dizisindeki üyelerin elemanlarına erişmek aynı yapıların elemanlarına erişmek gibidir. Örneğin kutular dizisindeki 5 numaralı elemanın boy değerine ulaşalım.

deger = kutular[5].boy;

Değişkenlerin işaretçilerinin olduğunu biliyoruz. Ama aynı zamanda her yapı tipinin de bir işaretçisi bulunur ve yapı tipini işaret eder. Yukarıda bahsettiğimiz ok operatörünü (->) işte bu durumlarda kullanırız. Şimdi bir kutu yapısının işaretçisini tanımlayalım.

struct kutu *kutuptr;

Ayrıca bu işaretçiye değer atamak için addressof(&) operatörünü yapıların adresine erişmek için kullanabiliriz. Örnek bir kullanım şu şekilde olabilir.

kutuptr = &kutu1;

Bu durumda kutu1’i gösteren kutuptr yapı işaretçisinden kutu1’in örneğin en değerine erişmek istiyorsak şu şekilde bir kod yazarız.

deger = kutuptr->boy;

Yukarıda yazdığımız kod aslında (*kutuptr).boy komutuna eşdeğerdi. Yani * operatörüyle kutuptr’nin işaret ettiği yapı değişkeninin kendisine ulaşıyoruz sonra ise (.) operatörüyle elemanının değerine ulaşıyoruz. Ama genellikle böyle bir kullanım yerine ok operatörü (->) kullanılmaktadır. Gömülü sistemlerde çalışırken bunu örneğin STM32’nin HAL (Donanım soyutlama katmanı) kütüphanesinde bolca görebilirsiniz. Ne yazık ki pek çok kaynak ve kitap ok operatörünü es geçmektedir. Biz temel C programlama derslerinde bile bundan bahsetme ihtiyacı duyuyoruz.

Yapılarla ilgili bir diğer özellik de aynı tipteki iki yapı değişkeni birbirine atanabilir. Yani istersek kutu1 = kutu2 şeklinde bir kullanımda bulunabiliriz.

Yapılar hakkında anlatacaklarımız bu kadardı. Bir sonraki yazıda yapılar ile alakalı uygulama yapıp, argüman olarak yapıların nasıl aktarıldığını göstereceğiz. Ayrıca yapılar ile doğrudan alakalı olan typedef tip tanımlamasından bahsedeceğiz.

Last updated