Gömülü C – 12 : Yapılar

Merhabalar, bugünkü yazımda C dilindeki yapılardan bahsedeceğim. Ancak bu bahsediş esasen bir girizgah şeklinde olacak. Çünkü yapısal bir dil olan C dilinde, tasarımın temel yapı taşı, bir manada tuğlası, sıvası yapılar olduğundan bu yapıları tek bir yapıda anlatmak oldukça zor olacaktır. Bunu yapmaya çalışıp eksik çok sayıda nokta bırakmak yerine, önce çok derinlere inmeden sizlere yapısal tasarım elemanlarnı ve bunların temel psikolojilerini aktarıp, ardından ilerleyen konularda bunların sanatsal kullanımlarını sunmanın peşindeyim. Bunun daha faydalı olacağına inanıyorum. Dilerim sizlere azami faydayı sağlayacak formatı, bir şekilde tutturabilirim 🙂

Bence bir yazı, lap diye konuya dalmadan önce ne vadettiğini anlatmalı, amacını açıklamalıdır. Ben de struct, enum, union diye başlamadan önce sizlere bunlarla ne yapacağımızı aktarmaya çalışacağım. Haydi başlayalım…

Daha önce de söylediğim gibi, bir programın amacı, gerçek hayattaki bir problemi çözmektir. Gömülü sistemlere özgü olarak sunulan çözüm, gerçek hayatı fiziksel olarak doğrudan etkiler. Yani bir gömülü sistem, genellikle hayatla doğrudan temas halindedir. Gerçek hayattaki bu problemin çözülebilmesi için, sisteme problemin ve çözümün tanımının, çok iyi yapılması gerekir. İşte gerçek hayatın unsurlarının, bizim tasarım ortamımıza aktarılması işine modelleme denir. Modelleme, mühendisliğin en temel konularından biri olmakla birlikte, tasarımın olmazsa olmazıdır. Lafı uzatmayayım. C dilinin yapısal bir dil olarak sınıflandırılmasının sebebi, modellemenin yapılar ile yapılmasıdır. Bu vesileyle bir dile sınıfını verecek kadar önemli bir konuya geçtiğimizi belirtmek istiyorum 🙂 Yapıları hakkıyla kullanmayı bilmeden, karşılaştığımız problemlere güzel çözümler üretebilmemiz mümkün olmayacaktır. Bu sebeple lütfen, bu yazıdan ya da başka bir kaynaktan, yapıları hakkı ile öğrenmeye, hakkıyla biliyorsanız bile bu işi bir adım da olsa ileriye taşımaya gayret edininiz.

Şimdi, C dilindeki struct, enum ve union’un suyunu sıkmaya başlayalım. Bakalım neler çıkacak 🙂

STRUCT

Struct, gömülü yazılımda çok çok çok büyük öneme sahip. Bu sebeple affınıza sığınarak, uzatarak da olsa basitçe açıklamaya çalışacağım. Bunu yaparken diziler ile analoji kurarak struct’u sizlere açıklamaya çalışacağım.

Bildiğiniz üzere diziler, aynı veri tipindeki birden fazla değişkeni bellekte saklamak için kullanılırlar. Ancak bu veri güruhu ya da veri dizisi bellekte saklanırken yan yana saklanır. Yani çok sıra dışı bir durum yok ise bir dizinin tüm elemanları belleğe ardı sıra yerleşir. Dizi tanımının en önemli iki özelliğini bu bağlamda tekrar sıralarsak :

  1. Dizinin tüm elemanlarının aynı veri tipinde olması
  2. Dizinin elemanlarının belleğe ardışıl yerleştirilmesi.

Daha dizilere değinmedim ama, bu iki özellik diziler hakkında ayan beyan görülen ilk iki özelik. Struct veri tipi de (dizilere benzer şekilde) bir veri topluluğunu ifade eder ancak bu topluluktaki her bir veri (değişken), farklı tipte olabilir. Yine struct içindeki bir veriye ( yani struct elemanına), dizi indeksi gibi bir indeks yerine, bu verinin ismiyle erişilir.

Struct, birbiriyle ilintili birden fazla farklı kavramı birleştirerek bir context(konu) çerçevesinde toplamaya yarayan çok güçlü bir modelleme aracıdır. Güçlü olmasının sebebi ise, farklı veri tiplerini aynı yapı içinde paketleyebilmesinden gelir.

Struct’un bellek kullanımı dizilere benzerdir yani struct içindeki elemanlar belleğe ardışıl olarak yerleşir. Ancak eğer struct içindeki elemanlar, kodlamanın yapıldığı hedef platformun adres genişliğine tam oturmuyorsa, struct alignment problem denilen problem ortaya çıkar. Bu durumda, struct elemanları bellek adresinin bir veya bir kaçını tam olarak dolduramadığından, derleyici bu sorunu gidermek için arada kaydırmalar ya da boş yer bırakmalar gibi operasyonlara gider. Bu durumun bilincinde olmakta büyük faydalar var.

Struct tanımı birde farklı şekilde yapılailiyor. Dilim döndüğünce her birini anlatmaya çalışacağım.

Etiketlenmiş (tagged) struct tanımı aşağıdaki gibi yapılır:

Yukarıdaki örnek kodda, yeni tanımlanan struct’ın etiketi Part’dır. Ve bu struct’un elemanları sırasıyla number, on_hand, name ve price’dır. Bir struct içindeki elemanların tümünün farklı tipte olabileceğini ancak bunun bir zorunluluk olmadığını lütfen unutmayınız.

Bu noktada, “struct Part” tanımının, bir veri tipi olduğunu ve “struct Part” tipinden değişkenlerin yaratılabileceğini unutmayınız. Örneğin “sruct Part MonopolyGame;” dediğimizde Part tipinde bir monopoly oyunu yaratmış oluruz. Ve örneğin bu oyunun fiyatına şöyle erişebiliriz.

Etiketli struct tanınımda, struct tipini tanımlarken aynı zamanda değişkenleri de tanımlamak mümkündür.

Gördüğünüz gibi yukarıdaki kodda joe, sue ve mary isimli üç öğrenci tanımlanmış. Tabi bu tanımları yukarıdaki şekilde yapmak, sonradan yeni elemanlar tanımlanamayacağı anlamına gelmez. Pekala da sonrasında da yeni elemanlar tanımlayabiliriz.

Yukarıdaki yönteme ek olarak, struct tipi tanımlamadan, struct şeklinde değişkenler tanımlamak da mümkündür. Ancak bu durumda, aynı tipten yeni değişkenler tanımlamak mümkün olmayacaktır. Aynı zamanda bunların bir fonksiyona argüman olarak verilmesi de kolay yoldan mümkün olmayacaktır. Esasen bu da akıllıca değerlendirilip, kullanımı kısıtlama amaçlı olarak kullanılabilecek bir özelliktir. Ancak bu durum, tekrar kullanılabilirliği de öldüreceğinden dikkatlice kullanılmalıdır. Şimdi bu tarif ettiğim duruma bir örnek vereyim:

Temel anlamda struct tanımını öğrendik. Şimdi bir sonraki seviyeye geçelim 🙂

Typedef’in Struct ile Kullanılması

typedef, programlama yapan kimselerin kendi veri tiplerini tanımlayabilmelerini sağlar. Bu veri tipi, basit veri tipi (uint8_t gibi) olabileceği gibi; complex (birden fazla  veri tipini aynı anda içinde barındıran) yapıda da olabilir.

İyi de yazılımcı kendi veri tipini tanımlamaya neden ihtiyaç duysun ki? Çünkü temel veri tipleri sadece genel amaçlar içindir. Ve modellenen problemler genellikle özel problemlerdir ve bu probleme özgü bilgi setlerini barındırırlar. İşte bu bilgi setlerini modeleyebilmek ve bu modeli tekrar kullanabilmek için typedef sözcüğünden faydalanırız.

Hemen örnek verelim:

Yukarıda Integer olarak adlandırılan, aslında int tipinin aynısı olan yeni bir veri tipi tanımlanarak bu veri tipindn 3 adet değişken yaratılmıştr. Böyle bir şey ilk bakışta saçma gelebilir ancak burada büyük ibretler vardır. Misal verecek olusak:

  1. Integer isimli bir veri tipi, okunabilirlik açısından int’den çok daha iyidir.
  2. Gerek duyulursa typedef ileride farklı şekilde değiştirilebilir. Örneğin “typedef long Integer”. Bu sayede Integer tipinden tanımlanmış tüm değişkenler bundan etkilenir. (Bunun dezavantaj oluşturduğu durumlar da vardır)

Ayrıca bu sayede, örneğin C dilini Türkçeleştirmek bile mümkündür 🙂

Hemen misal verelim:

Epey eğlendik ama artık C üzerine kurulmuş küçük bir programlama dilimiz oldu. Hem de  TÜRKÇE!

Tüm bu eğlenceye ek olarak, typedef tanımının asıl gücü, kompleks veri tiplerinin tanımlanmasında ortaya çıkar. Bu da tam olarak struct’ı anlatmaktadır.

Diğer değişkenler için geçerli olan scope kavramı, struct için de aynen geçerlidir. Typedef olsun ya da olmasın, struct tipi tanımlandığı yerin scope’u kadarlık bir bölgede tanımlı olur. Ne demek istiyorum? Misal typedef struct ile yapılan bir veri tipi tanımı bir fonksiyonun içinde yapıldıysa, o değişken tipi ancak o fonksiyonun içinde kullanılabilecektir. Her yerde kullanılmasını istediğiniz bir veri tipi tanımlamak için, onu h dosyasında tanımlamanız yeterli olacaktır. Bu sayede o h dosyasını include ile ekleyen her c dosyası, tanımlanan bu yeni veri tipini kullanabilecektir.

Şimdi bizim öğrenci örneğini typedef ile yapalım.

Artık, Student tipi yeni bir veri tipi olarak tanımlandığından, Student tipinden yeni değişkenler yaratacağımız zaman artık bir daha struct yazmaya gerek kalmaksızın değişken tanımı yapabileceğiz. Misal şöyle:

Yukarıdaki örnekte, struct’un etiketi ile veri tipi aynı tanımlanmıştır. Bu mümkün olmakla birlikte zorunlu değildir. Aşağıdaki tanım, yukarıdaki ile aynıdır.

Ben genelde struct tanımlarını yukarıdaki gibi yapıyorum. Kullanımı daha kolay geliyor 🙂 Aynı zamanda kod bence daha anlaşılır oluyor ancak bu kısmı tartışmaya tamamen açık bir yorum.

Struct’a İlk Değer Ataması Yapmak ve Struct’ların Birbirine Atanması

Struct tipinde olan değişkenlere de farklı şekillerde ilk değer ataması yapılabilir. Süslü parantez içinde, her bir eleman sırası ile virgül ile ayrılarak ilk değer ataması yapmak mümkündür. Misal verelim:

Yukarıdaki örnekte susie adlı  öğrencinin gpa yani not değeri girlmediğinden ilk değer olarak 0.0 değerinin not olarak atanması beklenir. Ancak bu beklenti gerçekleşmeyebilir. İlk değer ataması yapılmayan değerler her zaman risk unsurudur ve mümkünse tüm verilere ilk değer ataması yapılmalıdır. Neyse 🙂

İlk değer ataması bundan farklı şekillerde de yapılabilmektedir. Örneğin Linux kaynak dosyalarında sıklıkla göreceğiniz bir değer atama şekli aşağıdaki gibidir:

Yukarıdaki yöntem C’nin C99 destekleyen derleyicileri ile mümkündür ve bu sayede struct içindeki değişkenlerin sırası önemsiz olmaktadır. Çünkü zaten “.isim” ile hangi elemana ilk değer ataması yaptığımızı belirtmiş oluyoruz. Bu yöntemin, diğer yönteme göre çiçek gibi bir güzelliği vardır. Haydi diyelim bir struct tanımladınız, sonra günlerden bir gün içiniz elvermedi ve structa bir eleman daha eklediniz. Eğer sıralı ilk değer ataması yaparsanız (ilk örnekteki gibi) başınız dertte demektir çünkü yarattığınız her bir sturct değişkeni için sırayı tek tek bulup doğru yere ilk değer atamasını yazmanız gerekir. İkinci yöntemde ise koyun noktayı yazın yeni değişkenin adını, atayın değeri ve arkanıza yaslarken build tuşuna basın 🙂 Linuxcu dayılardan alınacak çok ibret var ama, burada da bu yöntemi seçerken bir bildikleri olduğunu görüyoruz.

Struct’ların Birbirlerine Atanması

Aynı tipteki iki değişken birbirine atanabilir. Bu bağlamda aynı struct tipindeki iki değişken de doğrudan birbirlerine atanabilecektir. Yalnız struct’ların birbirlerine atanmaları çok sıkıntılı ve dikkat edilmesi gereken bir mevzudur. Hatta struct’un herhangi bir şeye atanması dikkatle yapılması gereken bir işlemdir ve bazı riskler barındırır.

Ne demek istiyorum? Diyelim ki sturct içinde bir pointer var. Bu pointer bir yer işaret ediyor. Bir struct değişkenini bir başkasına atamak, o structu kopyalamak anlamına gelmez. Sadece iki sturct’un da aynı yeri göstermesi anlamına gelir.

Bu kısım biraz uzayacak olsa da, önemli olduğundan detaylıca anlatmaya çalışacağım. Bizim Student sturct’ının tipindeki değişkenleri birbirine atamak/kopyalamak filan isteyelim. İlk anda akla gelen 3 temel yol var.

İlk yöntem, camiada amele yöntemi olarak (asla hakaret/aşağılama amaçlı değildir!) bilinir ve çok da tercih edilmez. Sonuçta Veli’nin tüm bilgilerini Ali’ye atamak istiyorsak bunun daha güzel yolları var.

İkinci yöntem atamadan çok kopyalamadır. Adı üstünde iki değişkenin işaret ettiği ya da tutulduğu bellek alanlarını verilen uzunluk (sizeof Student) kadar byte kopyalar. Bu olay hard copy olarak geçer ve bellek içeriği tamamen kopyalanır. Yani aynı bilgileri taşıyan iki farklı adres bölgesi olur. Bu yöntem çok belayı def eder. Ali’nin bilgilerini Veli’ye aktarıp sonra Veli’yi okuldan kovmak istiyorsanız bu yöntem birebirdir çünkü Veli’yi kovsanız bile, Veli’nin bilgileri Ali’nin aklında ayrı bir yerde saklanmış durumda olur.

Üçüncü yöntem kısa gözükmekle birlikte derleyiciye çok daha fazla optimizasyon şansı bırakır. Bu da beklemediğiniz bazı davranışları beraberinde getirebilir. Okunurluk açısından da iki değişkeni birbirine atamak, kopyalamaktan daha farklı bir mesaj verir ve doğal olarak = operatörü burda kopyalama değil atama işi yapar. Yani bizim Veli bildiğimiz kişi artık aynı zamanda Ali’dir. Bu durumda Veli’nin başına birşey gelirse Ali’nin de başına bir şey gelecektir. Bu da çok büyük bela demektir.

Sizlere nacizane tavsiyem, bu yöntemler üzerinde denemeler yaparak davranışı bir kez de kendiniz görünüz. Sonra bir gün başınıza gelir, öyle okuyup geçerseniz hatırlamazsınız bile 🙂 Bu konunun oturması için bu atamaları yapıp, altında printf ile struct adreslerinin ve struct elemanlarının yazdırılması şart. Belki aranızda sturct adresi nasıl yazdırılır bilmeyen olabilir, onun için de hemen şöyle ufacıktan yazıverelim:

Başınız ağrımasın istiyorsanız, yukarıdaki amaç için yöntem 2’yi öneririm. Ne yaptığınızı çok iyi biliyorsanız yöntem 3’ün de avantaj getireceği durumlar vardır. Misal daha az bellek kullanırsınız. Ama çok dikkat etmeniz gerekir. Hem de eğer kodu takım olarak geliştiriyorsanız, sizin çok dikkat etmeniz yetmez, arkadaşlarınızın da aynı dikkati göstermesi gerekir. Eğer takım arkadaşlarınızı seviyorsanız, onlara dikkat etmeleri gereken yeni şeyler vermeyin. Zorlaştırmayın, kolaylaştırın 🙂

Struct’ların Pointer(İşaretçi) ile İmtihanı

Şimdi daha işaretçileri anlatmadım ama, zaten bu yazı dizisinin doğası gereği sizin onu referans verilen kaynaklardan ya da başka yerlerden öğrenmiş olmanız ya da zaten biliyor olmanız gerekiyor. Ki burada olayın biraz daha ince detaylarını, felsefi yönlerini değerlendirelim 🙂

Pointer yani işaretçi dediğimiz nane, herhangi bir şeyin adresini tutabilir. Zaten işaret ettiği şey de o adresin içindeki veridir. Pislik yapıp farklı türde işaretçiler ile bambaşka türden verileri işaret etmek de çirkin olsa da mümkündür. Ama öyle şeyler yapmayacağız, pointer hangi tipte veriyi işaret edecekse o tipi gösterecek.

Örnek kodumuzda, sptr değikeni struct Student* (struct Student işaretçisi) tipinde bir işaretçidir ve haliyle struct Student tipinden bir veriyi işaret etmek ister. Nitekim bu işaretçi, isteğine kavuşup alice’i işaret etmiştir. Struct’ları fonksiyona argüman olarak filan verecekken tüm veriyi kopyalamak feci derecede kötü bir tercihtir. Bu gibi durumlarda struct’ın kendisini kopyalamak yerine, bellek adresini kopyalamak daha mantıklıdır. Misal bizim Student struct’ı nereden baksanız 28 Byte filan vardır. Oysa 32-bit’lik bir platformda her adres 4 Byte’dır. Bu sayede fonksiyona bu bilgiyi aktarırken 28 byte yerine 4 byte kopyalayarak, tam 24 byte kara geçmiş olduk.  Böyle güzide bir fonksiyon örneğini aşağıda hizmetinize sunuyorum.

Bu fonksiyonu çağırmak için de aşağıdaki gibi  adresi(&) operatörünü kullanabiliriz.

Dikkat ederseniz implementasyonda myStudent.name yerine myStudent->name yaptık. Bunun sebebi, myStudent bir pointer olduğundan onun gösterdiği bellek alanını struct olarak düşünüp oradaki name değerine ulaşmayı sağlayan operatörün -> operatörü olmasıdır. myStudent değişkeninin tipi struct olmadığından (çünkü struct işaretçisidir), nokta ile elemana ulaşmak doğal olarak mümkün olmaz. Ancak myStudent dediğimiz zaman artık  (myStudent) ifadesinin tipi bir struct olacaktır. Dolayısıyla onda nokta ile erişim yapmak aşağıdaki tüm şekillerde mümkündür.

Bizim yapılar (struct’lar) içlerinde temel tiplerin ya da kompleks tiplerin işaretçilerini taşıyabilir. Görürseniz garibinize gitmesi için bir sebep yoktur.

Sturct içinde işaretçi görürseniz, sturct ataması yaparken iki defa düşünün ve yöntem 2’yi aklınızda tutun lütfen 🙂

Struct Bit Erişimi

Bu kısım çok çok çok çok önemli. Dataheet modelleme ve sürücü yazma işlerinde burayı bilhassa çok kullanacağız. Eğer bu konuda öğrenebileceğiniz yeni bir şeyler var ise, lütfen dikkatle okuyunuz.(Zaten okumadan bunu kimse bilemeyecek 😀 )

C dilindeki standart veri tipleri ile, en küçük BYTE erişimi yapabilirsiniz. Oysa stuct ile belleği bit bit modellemek mümkündür. Bu hem bellekten kazanç sağlar, hem de örneğin datasheet modellemesinde, çiçek gibi modelleme yapma imkanı sunar. Datasheet modellemeyi ileride anlatacağım ancak, lütfen bu kısmın çok önemli olduğunu unutmayınız, unutturmayınız.

Hemen hızlıca örnek kodu yazalım.

is_element:1 demek, is_element değişkeni 1 bit yer kaplayacak demek. Bu durumda bu değişken gerçek bir bool oluyor. atomic_number:8 demek, atomic_number 8 byte yer kaplayacak demek. Bu durumda en fazla 255’e kadar değer alabilecek bu numara.

Her şey tamam da uint32_t ve reserved niye var. Sebebi şu; 1 bit işaret edebiliyorsunuz diye bellek içindeki her bir biti canınız istediği şekilde yönetemezsiniz. Ne demek istiyorum? 32 bit’lik bit platformda her bellek gözü 32’bit olacaktır. Ve siz bir bit’lik bir sturct tanımlasanız bile, daha önce bahsettiğim sturct alignment problemi yüzünden geriye kalan 31 bit israf olacaktır. Derleyici burayı optimize etmek isterse de vay halinize çünkü sizin 1 bitiniz ciddi olmayan bir azınlık olduğundan, çoğunluğun bekası için feda edilebilir. Bunu engellemek için struct’umuzun boyutunu 32 bite tamamladık. Nasıl yani; 1+1+1+1+4+8+16 = 32 oldu. 32 bite tamamlayacağımızı peşin peşin söylemek içinde  her elemanın tipini uint32_t: yaptık. Bu sayede hem olası yanlış optimizasyonları engelledik hem de kimyasal envanter için gerekli onca bilgiyi 32 bit’de sakladık. Peki böyle yapmasaydık da aşağıdaki gibi yapsaydık ne olurdu?

Bu durumda bizim kimyasal envanter 7 byte yani 56 bit tutacaktı çünkü bool tipi asla 1 bit tutmaz genelde 1 byte tutar. Struct memory alingment olacağından bu itina ile 64 byte’a tamamlanacaktı. Yani tam 2 katı bellek kullanmış olacaktık. 1000 envanterde kaybedine tam 4000 Byte 🙁 Hiç gerek yok 🙂

Sturct için ilk turda bu kadarı yeterli.

ENUM

Enumerasyon diye Türkçeye ittirilmeye çalışılan bu terim aslında etiketlenmiş sayı yığınından başka birşey değil.  Benim bildiğim tam bir çevirisi olmamakla birlikte, sayım etiketi olarak isimlendirmek mümkün olabilir. Ya da kısaca enum da denebilir. Enum sayı gibidir demiştik ama bazı farkları var tabi (o farknan da çok güzel oldu). Enum tipinde:

  • Sadece önceden belirlenmiş sabit değerlere izin verilir.
  • Tanımlanmış her bir değer isim ikilisi vardır. Bu sebeple enum ile çalışırken sayılar yerine ilgili değere karşı düşen isim kullanılır.

Burada çokca feyizler vardır. “#define ile benzer bu” diyenler olabilir. Oraya da geleceğiz.

Hemen bir kaç tanımlama örneği patlatalım yine:

 

Ben yine yazım olarak ve tanım olarak aşağıdaki şekli daha şık buluyorum:

Burada oyunun kuralları basit:

  • Herhangi bir enum elemanına, özel bir değer atanabilir: misal E_DIRECTION_NONE sayım etiketine 0 değeri atadık.
  • Değer atanmayan sayım etiketleri, bir önceki etiketten bir fazla değer alır. Buna göre E_DIRECTION_NORTH etiketinin değeri 1, E_DIRECTION_SOUTH etiketinin değeri 2 oldu vs.
  • Eğer ilk etikete değer atanmadıysa, ona otomatik olarak sıfır değeri atanır.

Esasen size bir sır vereyim, aynı değeri birden fazla etikete atamak mümkündür. Ama inanın bana bu alenen çirkinleşmektir, ve çirkinleşmenin hiç ama hiç luzumu yoktur!

Enum ve Define Karşı Karşıya

Hızlıca özetlemek gerekirse; enum bir veri tipidir, define ise derleyicinin kopyala yapıştır yaptığı bir etikettir. Doğal olarak enum tipinde değişken tanımlayabiliyoruz, nitekim tanımladık. Bu değişkenler doğal olarak RAM’de tutulur (const tanımlanmadılarsa!). Define her yere aynen yapıştırılacağından, genişletilebilirlik açısından enum kullanmak çok faydalıdır. Enumerasyon yapmak için define kullanmayınız, enum kullanınız. Etiketleme (define) yapmak için enum kullanmayınız, define kullanınız.

Diyelim bir durum makinasının durumlarını saklayacaksınız. Burada enum kullanmak gerekir çünkü eğer define kullanırsanız, yarın yeni bir state eklendiğinde ve onun ara değerde olması gerektiğinde, tüm define’ları teker teker değiştirmeniz gerekir. Oysa enumda rastgele bir yere yazdığınızda su akacak ve yolunu en güzel şekilde bulacaktır.

Şimdi enum ile ilgiliverdiğimiz bu örneği kod olaraktan aktaralım:

Gördüğünüz üzere çiçek gibi oldu çiçek 🙂

UNION

Geldik üç silahşörlerin üçüncüsüne… Union yani birlik!

Bizim unionlar aynen struct gibi tanımlanır ve kullanım şekilleri (kullanım yerleri değil) çok çok benzerdir. Ancak çok önemli bir fark vardır:

Struct’lar içlerindeki verileri ardışıl olarak saklayabilecek kadar belleği kendilerine ayırırlar. Union’da ise union’un boyutu, union içindeki en büyük boyutlu elemanın boyutu kadardır çünkü union’un tüm elemanları aslında belleğin aynı gözünü (aynı adresi) gösterirler. Bu özellik başta işe yaramaz gibi gözükse de, özellikle verinin bilgiye dönüştürülmesinde yani yorumlanmasında çok işe yarar şekilde karşımıza çıkmaktadır.

Hemen ibretlik bir kod paylaşalım.

 

Burada union kullanılarak 4 byte’dan kar edildi. Çünkü aynı uçuş sturct’ı içinde eğer uçuş tipi kargo ise float olarak tonaj değişkeni, eğer uçuş tipi yolcu ise uint32_t olarak nPassengers (yolcu sayısı) değişkeni aynı bellek gözünü gösterir. Bu olay, inanılmaz zekice bir olaydır ve anlaşılmayı hak etmektedir.

Burada union kullanılmasa idi, haybeye 4 byte harcanacaktı. Bu durumda nPassengers ve tonnages değişkenleri belleğe ardışıl olarak yerleşecekti. Halbuki düşününce böyle bir ihtiyaç gerçekte yok çünkü bu iki değişken asla aynı anda kullanılamayacak. İşte burada union geldi ve bir Flight için 4 byte, 100 Flight için 400 byte kar etmemizi sağladı.

Kodun ne kadar karizmatik olduğuna değinmiyorum bile. Belki oradan alıp yürüyecekler de olabilir.

Hemen eski yazılarımızda açıkladığımız ibretlik bir örneği daha sizlerle paylaşıyorum:

Burada da union ile yine bit kaydırma, maskeleme işlerini hiç yapmadan 32 bitlik bir veriye hem byte byte, hem 2 byte 2 byte hem de tümden erişebiliyoruz. Bu olayı da özellikle haberleşme yazılımlarında sıkça kullanacağız. Örnek kodlarla bunları denemeyi unutmayınız 🙂

Yazıları beğendiyseniz eğer,  faydalanabilecek arkadaşlarınızla da paylaşabilirseniz sevinirim 😉

Şimdi devam…

Önceki Sayfa   Sonraki Sayfa

 

Leave a Reply