Stockholm Gezi Rehberi

Merhabalar,

Bu blogu açtığımdan beri yazdığım yazılar hep teknik içerikli oldu. Bunlar devam edecek ancak, sosyal içerikli bazı paylaşımlar da olacak. Umuyorum ki bu Stockholm Gezi Rehberi’nden faydalanacaklar olur.

Öncelikle en tatsız kısımdan başlayayım. Stockholmde euro geçmiyor. Para birimleri SEK. Ama tek kuruş nakit olmaksızın her şeyi yapmak mümkün. Çünkü en küçük dükkanından otobüsüne kadar her yerde kredi kartı geçiyor ve mobil pos cihazları var. Tek kuruş nakit bile gereksiz.

Stockholm uçuşları genelde Arlanda hava limanı üzerinden yapılıyor. Arlanda, Stockholm merkeze bir miktar uzak ama ulaşım imkanları çok geniş. Stockholm içi tüm ulaşım SL card ile yapılıyor. Bu kart, İstanbul Kart’a benzer bir Rfid kart. Toplu taşımadan faydalanmak için almak gerekiyor çünkü tekli biletler bunlara göre epey pahalı. Arlanda hava limanındaki turist ofisinde SL card satışı ve yüklemesi yapılıyor. Aynı yerden ücretsiz turist haritasını da almakta fayda var çünkü harita çok çok iyi hazırlanmış. Arlanda hava limanının hemen çıkışında Flybusarrna otobüsleri doğrudan şehir merkezine gidiyor ancak bunlarda Sl Card geçmiyor. SEK ya da kredi kartı ile ödeme yapmak mümkün. Tek yön 150 SEK diye hatırlıyorum. Stockholm’e gidişlerde ben, Arlanda Hava Limanı’nda SL Card’ı 3 günlük ya da 1 haftalık sinirsiz olarak doldurmayı tercih ediyorum. Bunu  öneririm. 3 günlük sınırsız yolculuk 230 kron iken 7 günlük sınırsız yolculuk 300 kron. Ben ilk aldığım kartı halen sakladığımdan Sl Card’ın kendi fiyatını hatırlamıyorum.

Arlanda hava limanından SL card ile Stockholm merkeze gitmek mümkün. Bunun için hava limanının hemen çıkışında 5 numaralı duraktaki 583 Marsta Station otubüsüne binip son durak olan Marsta’da inip, oradan da hemen J36 treni ile Stockholm T Centralen’e yani merkeze varmak mümkün. SL card ile yapılan bu ulaşım totalde Flybusarrna’dan daha hızlı. 1 saat gibi bir surede merkeze varılıyor.

Stockholm merkez tren istasyonu (T Centralen) her yere bağlanan bir ulaşım merkezi. Ancak otele eşya bırakmak vb. gerekmiyorsa geziye buradan başlamakta fayda var. İstasyonun çıkışından Old Town olarak da bilinen Gamla Stan’a gitmek mümkün. Gamla Stan turistik bir yer ve mimari açıdan çok güzel. Bilhassa kahve dükkanlarını öneririm. Keza hediyelik eşya dükkanları da bu bölgede. Gamla Stan’ın tam ortasında Nobel Museum bulunuyor. Nobel tarihine ilgi duyulması durumunda tavsiye ederim.

Stockholm’de gezilmesi gereken bölgeler Gamla Stan, T Centralen bölgesi, Vasastan bölgesi, Skeppsholmen, Djurgarden bölgesi ve zaman kalırsa da Langholmen. Stockholm coğrafi olarak da külterel anlamda da mükemmel bir şehir ve ayni mimari tüm bu bölgelerde göze çarpıyor. Tüm güzel yerler Gamla Stan’ın komşuluğunda 🙂

Müze olarak Fotografiska Museum, Nobel Museum ve National Museum’u sırasıyla tavsiye ederim. Fotografiska Museum’un içindeki cafe’nin efsanevi bir manzarası var. Fiyatlar da uçuk değil. Tavsiye ederim. Yine Skeppsholmen adasının sonundaki müze’nin restoranı da çok çok iyi bir manzaraya sahip. Buraların manzaraları, İstanbul’daki boğaz manzaralı çoğu restorandan çok daha güzel diye düşünüyorum.

Stockholm yemekler açısından zayıf ama göreceli olarak pahalı bir şehir. Hard Rock Cafe şehirdeki güzel mekanlardan biri ama diğer Hard Rock Cafe’lere oranla biraz vasat ve sönük. Yine de güzel. Kişi başı ortalama fiyat burada 350 SEK civarında. Ambiyansının yine iyi olduğunu söyleyebilirim. Yemekler de lezzetli.

İyi restoranlardan biri Pelikan Restaurant. Ama merkeze biraz uzak ve rezervasyon yaptırmak gerekiyor. Geleneksel yemeklerden geyik eti en güzel bu mekanlarda yapılıyor. Ama geyik etini çok da tavsiye etmem sanırım 🙂

İsveç’de somon meşhurdur ama somonları Norveç ile kıyaslanamaz. Esasen sorunca İsveç’in deniz mahsulleri meşhur ama daha Tuzla balıkçısından iyi deniz mahsülu sunan bir İsveç restoranı görmedim. İçlerinde en iyisi Pelikan Restorant.

Beef, steak tarzı şeyler için çok sayıda iyi yer var. Barbeque Steakhouse ve Fridays Cafe iki iyi örnek. İkisi de T Centralen’e çok yakın.

McDonald’s ve Burger King de çok çok yaygın ve bir iki menü hariç tüm ürünler dana etinden. Fiyatlar Stockholm ortalamasına göre çok çok uygun.

Stockholm, kahveleri ile dünyaca meşhur. Çok yaygın olan Espresso House da oldukça güzel bir mekan.
Tatlıları ise genel olarak pek önermem. Buraları çok arayacağınızdan eminim.

Stockholmde kış sporları için de çokça imkan var ama bunlar kış dönemi için. Yaz ya da baharda yapılabilecek en güzel şey yemyeşil adaları gezmek. Doğa tam anlamıyla eşsiz.

Stockholm yanılmıyorsam 2007 yılında dünyanın en yaşanır şehri seçilmişti. Şehirde eğitim ve kültür seviyesi çok çok yüksek. Hemen herkes İngilizce de biliyor. İsveç kültürü Alman kültürüne yakin ama İsveç bana göre biraz daha önde 🙂

Çocukların bir birey olarak yetiştirildiğini günün her anında görmek mümkün. Ancak çok önemli bir nokta kimsenin çocuğunu “ne şirinmiş” filan diye sevmemek gerekiyor. Ne yazık ki pedofili olarak bakılıyor ve çok sert tepki verirler.

Sözün özu İsveç’te Gamla Stan ve çevresindeki adalarda gezinmekte fayda var. Stockholm’un sembol bir yapıtı bildiğim kadarı ile yok. Genel olarak şehrin dokusu, doğası, insanları ve kültürü çok güzel.

Şimdiden iyi yolculuklar dilerim.

XBee Kullanımı

Merhabalar arkadaşlar, eski blog sayfamı artık güncellemeyeceğimden, oradaki önemli bir yazıyı buraya taşımak istedim. XBee kullanımı üzerine yazdığım yazı, beklentilerimin ötesinde bir ilgi görmüştü ve zaman içerisinde özellikle resimlerin uçmasından ötürü çok sayıda kişi, resimleri güncellemem için benimle iletişime geçmişti. Bu vesileyle hem resimleri güncelleyeceğim hem de yazıyı buraya taşımış olacağım.

RF haberleşmesinin ne kadar sık kullanılan bir haberleşme teknolojisi olduğu su götürmez bir gerçek. Günümüzde RF haberleşme bir ham veri kümesini radyo dalgalarıyla aktarmaktan fazlasını ifade ediyor. Zigbee kavramı da burada ağırlığını ortaya koymuş, hatta ve hatta stratejik önemi haiz olmuş bir kavram olarak karşımıza çıkmaktadır (ne dedim !?  :D)

Zigbee aslında bir protololdür. Jennic firmasının Jennet protokolü gibi… xBee ise bu haberleşme protokolünü taşıyan bir donanımdır.

MaxStreamXBeeModules

 

Bu blog yazımda iki adet xBee’nin haberleştirilmesi işleminin nasıl yapıldığı ve gerekli alt yapı bilgilerini aktarmaya çalışacağım.
PAN ( Personal Area Network )
xBee modüllerinde tüm haberleşme işlemleri bir PAN üzerinden yapılmaktadır. Haberleşmenin gerçekleşebilmesi için mutlaka ama mutlaka bir PAN oluşturulmalıdır. PAN oluşturulmadığında hiç bir xBee cihazı faaliyet gösteremeyecektir. Bir PAN’ı ancak bir “Coordinator” oluşturabilir. PAN yönetimi ile ilgili bilgilere raporun ileri ki kısımlarında değineceğiz.
Bir PAN içinde bulunan tüm cihazların PAN ID’si aynı olmalıdır. Bunu daha teknik biçimde açıklayacak olursak aslında “coordinator” tarafından oluşturulan PAN’a katılmak isteyen tüm cihazların PAN ID’leri mutlaka “coordinator” cihazın PAN ID’si ile aynı olmalıdır. Aksi taktirde cihaz ağa kabul edilmez. Ağa kabul işlemleri ile ilgili daha geniş bilgilere ileri ki kısımlarında değineceğim.
Ağ Oluşturma:
xbee_create_pan
Yukarıda örnek bir PAN görmektesiniz. C, R ve E türü cihazların özelliklerini açıklayalım.
C , “Coordinator” :
Haberleşme kanalını ve PAN ID’sini belirler. Bir PAN’ın başlatılmasından C sorumludur. PAN’ı başlattıktan sonra C, E ve R’lerin PAN’a katılmasına izin verebilir. C veri alıp yollayabilir, ya da mesh network yapısında verinin bir sonrki cihaza iletilmesi işlemlerini gerçekleştirebilir. C tüm ağı yönettiğinden pil ile beslenmesi ve/veya düşük güç modlarında çalıştırılması kesinlikle uygun değildir. C sabit bir güç kaynağı ile beslenmeli ve “main power mode” dediğimiz standart güç modunda çalıştırılmalıdır. C’nin işlevini kaybetmesi, tüm ağın çökmesi anlamına gelmektedir.
R, “Router” :
Bir R’nin çalışabilmesi için öncelikle bir PAN’a katılması gerekir. PAN’a katıldıktan sonra R, diğer R’lerin ve E’lerin (End Point) PAN’a katılmasına izin verebilir. R veri alıp yollayabilir ya da mesh network üzerinde veri aktarımını sağlayabilir. R’ler diğer cihazların PAN’a katılmasına izin vereceklerinden düşük güç modlarında çalıştırılmaları uygun değildir.
E, “End Device”:
R’de olduğu gibi E’de çalışabilmek için öncelikle bir PAN’a katılmalıdır. E diğer cihazların PAN’a katılmasına izin veremez, ancak mesh network üzerinde veri iletimi yapabilir.
Cihazların işlevlerini okuduktan sonra, yukarıdaki iletişim diyagramının tekrar incelenmesi faydalı olacaktır.
Xbee Konfigürasyonu
xBee’lerin konfigüre edilmesi için bir çok araç bulunmamaktadır ancak bunlar içerisinde en çok kullanılan ve en güvenilir olan digi firmasının bizzat ürettiği x-ctu yazılımı-dır. Tamamen ücretsiz olan bu arayüz ile xBee ile ilgili hemen her işlemi gerçekleştirebiliyo-ruz. Bu program üreticinin web sitesinden indirilebilir.
C Konfigrasyonu:
C’yi broadcast modda çalıştırıyoruz. Bunun için yapmamız gerekenler:
PAN ID belirlenir, (ID)
Scan Channels belirlenir, (SC)
Scan Duration belirlenir (opsiyonel) (SD)
Destination Low Adress belirlenir: DL = 0xFFFF yapılır.
Destination High Adress belirlenir: DH = 0 yapılır.
NI belirlenir: NI = COORDINATOR
İlgili konfigrasyon ekranı aşağıdaki gibidir:
xbee_c_config
R Konfigrasyonu:
R unicast modda programlanmalıdır.
SC ve ID değerleri seçilir (PAN’a göre yani C’ye göre)
CH ve OP kontrol edilir
MY belirlenir, bu routerın 16 bitlik network adresidir.
DH = 0 yapılır.
NI = ROUTERNAME yapılır
Ekran aşağıdaki gibidir:
xbee_r_config
ÇOKLU AĞ KURMA
xBee’ler ile mesh network kurmak için yapılması gerekenlere değineceğimiz bu kısımdaki işlemler temelde 2 ana başlık altında toplanabilir. Bunlardan bir tanesi AT komutları, diğeri ise konfigürasyon.
AĞ 1:
Kurmak istediğimiz basit ağ yapısı, 1 koordinatör ve 3 router cihazdan oluşacak. Öncelikli olarak koordinatör cihazdan yayın yaparak tüm router cihazlar ile aktif veri alışverişi kuracağız.
xbee_nw1
Yukarıda çizmiş olduğumuz diyagramda görüleceği üzere koordinatör router cihazlara yayın yapmakta, bununla birlikte de router cihazlar koordinatöre veri yollayabilmektedir.
Cihazları yukarıdaki çalışma için konfigüre ederken, öncelikli olarak xctu yazılımında fonksiyon seti olarak ROUTER/END DEVICE AT seçilir. Daha sonra Show Defaults butonuna basılır. sonrasında da PANID belirlenir. Biz bu uygulamada PANID olarak 0x100 değerini girdik. 3 router’ı da konfigüre etttiğimizde operating PANID olarak gördüğümüz değer 0xFFFF olmalıdır. Bunun sebebi ise xBee’lerin aynı PANID’li bir koordinatör bulamaması ve ilgili PAN’ın açılmasını beklemesidir. Bu durumda Router’lara ilişkin konfigürasyon aşağıdaki gibidir:
xbee_cnf_nw1
Koordinatör cihazı konfigüre etmek için ise, function set kısmı COORDINATOR AT olarak seçilir. Daha sonra Show Defaults butonuna basılır. Bundan sonra da PAN ID seçilmelidir. PAN ID’yi yine 0x100 seçeceğiz. Daha sonra read butonuna bastığımızda operating channel’ın 0x0E olduğunu gördük. Bu değer farklılık gösterebilir.
xbee_cnf2_nw1
PAN oluşturulduktan sonra tekrar router cihazları xctu programı yardımıyla gözlemlemeliyiz. Bunun için yine ilgili modüle bağlandıktan sonra modem konfigrasyonu sekmesi açılmalıdır ve read butonuna basılmalıdır. Bunu yaptığınızda tüm modemlerin operating channel değerinin koordinatör cihaz ile aynı olduğunu göreceksiniz. Bu da router/end cihazların personal area network(PAN) içine başarıyla katıldığını göstermektedir.
Bu ayarları yaparken tüm modüllerin aynı seri haberleşme parametrelerini taşıdığından emin olmak gerekir, eğer bir modül handshaking isterken diğeri istemiyorsa ya da arada baudrate, parity, stop bit gibi farklılıklar varsa haberleşme gerçekleşmeyecektir. Bu haberleşmenin başarılı olduğunu aşağıda 1 defaya mahsus olarak gösteriyoruz, bundan sonraki ağ yapılarında bu şekilde olduğu gibi ekran çıktıları ile gösterim yapılmayacaktır. Benzer basamaklar kullanılarak tüm yapılar kolaylıkla test edilebilir.
 Koordinatör Xbee:
xbee_c_pan

Router 1 Xbee:

xbee_r_pan

Router 2 Xbee:
xbee_r2_pan
Router 3 Xbee:
xbee_r3_pan
Görüldüğü üzre planladığımız ağ yapısı beklentilerimize uygun olarak çalışmıştır. Yukarıda gördüğünüz mesajlardan kırmızı olanlar cihazın aldığı mesajlar, mavi olanlar ise cihazın gönderdiği mesajlardır. Buna göre koordinatör herkese mesaj göndermiş ve herkesten mesaj almıştır. Bu sayede ağ yapılarına ilişkin ilk uygulama tamamlanmış oldu.
AĞ 2 (Point to MultiPoint Bidirectional Network)
Bu ağ yapısı çoğunlukla kullanacağımız ağ yapısıdır. Bu ağ yapısı sayesinde bir merkezin kontrolünde istediğimiz haberleşme sistemini rahatlıkla kurabilmekteyiz. Ağ yapısı ise aşağıdaki gibidir:
xbee_cnf_nw2
Yukarıdaki sistemde koordinatör yayın yapmamaktadır. Router/End cihazlar doğrudan merkez ile haberleşmektedirler. Bu yapıda koordinatör bir anlamda server, router ise client gibi düşünülebilir. Her xBee ağında olduğu gibi burada da koordinatörün gücünün kesilmemesine dikkat edilmelidir.
Yukarıdaki yapı gibi bir çok farklı ağ yapısı düşünülebilir.Bu yapıların kurulmasında artık arayüzden ziyade AT komutlarından yararlanacağız. Bunun nedeni ise xBee modüllerini OHM’ler ile kontrol edecek olmamızdır. Dolayısıyla xBee modülüne ilişkin yapacağımız her türlü değişiklik seri haberleşme ile sağlanacaktır. Şimdi de yukarıdaki sistemi oluşturmak için yapmamız gerekenlere değinelim.
  1. Öncelikli olarak bir önceki uygulamada yani AĞ1’deki gibi bir yapı kurulur.
  2. Tüm router’lar için aşağıdaki işlemler yapılır
    1. Koordinatör cihaza terminal vasıtası ile “+++” komutu yollanır. Bu 2 saniyelik bir süre için xBee’yi komut moduna sokar.
    2. Zaman geçirilmeden “ATSH” komutu yollanır. Okunan değer not edilir.
    3. Hemen ardından “ATSL” komutu yollanır ve bu değer de not edilir.
    4. Daha sonra da “ATWR” komutu yollanır.
  3. Routerların tümü için sırayla aşağıdaki adımlar izlenir:
    1. “+++” komutu ile komut moduna geçilir.
    2. “ATDLkoordinatörSL” komutu işletilir. KoordinatörSL yazdığımız kısma 2. adımda not etmiş olduğumuz SL değeri yazılır. Örneğin “ATDL1A3252” gibi bir komut yollanabilir.
    3. “ATDHkoordinatörSH” komutu işletilir. KoordinatörSH yazdığımız kısma 2. adıma not etmiş olduğumuz SH değeri yazılır. “Örneğin ATDH402D5DA9” gibi bir komut yollanabilir.
    4. Bu adımlar tamamlanınca router koordinatöre veri yollamak üzere konfigüre edilmiş demektir. “ATWR” ile işlem sonlandırılır.
  4. Koordinatörden herhangi bir router cihaza veri yollamak için yapılması gerekenler 3. adımda router’a yaptığımız ile neredeyse aynıdır. Hangi router’a veri yollamak istiyorsak DH ve DL adreslerine onun adresini girebilir ve sırayla istediğimiz cihaza veri yollayabilir. Bu veri yollama rutininde xBee ağlarının tüm özellikleri devam eder.

 

Umarım işinize yarar 🙂

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

Şimdi devam…

Gömülü C – 14 : İşaretçiler 2

İşaretçiler (pointers) başlıklı yazımda konuyla ilgili güzel bir girizgah ve temel atma merasimi tertip ettik. Şimdi, işaretçilerle ilgili daha ileri konulara değinmenin tam sırası. İşin aslı işaretçilerin kullanım alanları çok çok geniş. Tilkiler normalde yalnız dolaşır ama işaretçiler değince aklımda bin bir tilki sürü halinde dolaşıyor. Hepsini burada yazmak çok mümkün mü bilmiyorum ancak, elimden geldiğince yazmaya çalışacağım.

Efendim özellikle belleği etkin kullanma noktasında, işaretçiler ziyadesiyle önemli bir faktör oluyor. Bu sebepledir ki gömülü programlama yapacak yiğitlerin işaretçilerin suyunu sıkması gerekmektedir. Mevzunun özütü yine aynı olsa da, her bir kullanım alanından alınacak sayısız ibretler olduğundan, farklıca örnekler üzerinden işaretçileri inceleyemeye çalışacağım. Yine de eksik kalan bir kullanım alanı olduğunu düşünüyorsanız, yorum olarak ekleyebilirsiniz.

Bu yazıyı okumayı bitirdiğinizde varacağımız noktada iki yol var.

  1. Bu şeyler sizler için yeni şeyler ise, bunları sindirdiğinizde o çok korkulan işaretçiler konusunda harbici bir uzman olmuş olacaksınız.
  2. Bu şeylerin tamamını zaten biliyorsanız,  zaten bir uzmansınız. Lütfen siz de blog yazın, haber edin biz de takip edelim yeni şeyler öğrenelim 🙂

Neyse şimdi derin bir nefes alıp başlayalım. Zira bu konular ciddi konular. Tek nefes yetmeyecek ama her nefesi derin almakta faydalar var.

Mutlak Adres İşaretçileri (Pointers to Absolute Addresses)

İşaretçilerin genel felsefesini bir önceki yazıda konuştuk. Genelde bir tipteki bir işaretçi, genelde aynı tipteki bir değişkeni işaret ediyordu. Peki ya bir işaretçi, sabit bir adresi işaret ederse?

Hmm. İyi güzel, etsin de niye etsin? Gömülü sistemlerde göreceksiniz ki, çalıştığınız platformun (mikrokontrolör, fpga, SoC, vs) sabit bir bellek haritası var. Yani ROM,RAM, çevreseller filan hep sabit adreslerden başlar. Doğal olarak, register’lar da sabit adreslere sahiptir.

Register Ne Ki?
İlk defa duyanlar için register; gömülü sistemde donanımın izin verdiği bazı donanımsal konfigürasyonları saklamak için kullanılan bellek alanıdır. Örneğin bir pinin giriş olarak mı, çıkış olarak mı konfigüre edildiği bir regsiter’da tutulur. Türkçe’ye “kütük” olarak çevrilmiştir ancak bu çeviri kütük gibi bir çeviri olduğundan, ben düzgün bir çeviri çıkana kadar register diyeceğim. 

Register’lar sabit adreslere sahiptir dedik. Öyleyse biz bunlara erişirken yani buraları işaret ederken aslında sabit adresleri işaret edeceğiz. Bu vesile ile mutlak adresleri işaret eden işaretçilerin nerede kullanılacağı hususunu apaçık bir şekilde gözler önüne sermiş olduk.

Öyleyse şimdi bir örnek üzerinden gidelim. Diyelim ki bizim mikrokontrolörün üç tane versiyonu var: A,B,C. Ve elimizdeki mikrokontrolörün 0xC0FFE nolu bellek adresinde bu kıymetli bilgi yazıyor. Bu bilgiyi nasıl okuyacağız? Hemen yazalım!

Notasyon ve kod gayet temiz diye düşünüyorum. Peki diyelim aynı mikrokontrolörün 0xFACE adresinde bulunan register da mikrokontrolörü başlatmak için olsun. Bizim mikrokontrolörü başlatmak için ille de 0xFACE adresine ‘S’ yazmak gereksin. Onu nasıl yapardık?

ÖNEMLİ NOT!
NOT: Yukarıdaki kodu bilgisayarınıza atıp çalıştırmayın diye return 0’dan sonraki virgülü sildim ve sistemin özellikle derleme hatası vermesini istedim. Bilgisayarımızda 0xFACE alanı korunan, özel bir bellek alanı olacaktır. Yukardaki koddan yalnızca ibret çıkarınız, kodu bilgisayarınızda çalıştırmanıza bence pek gerek yok 🙂 Çalıştırsanız da zaten yazma koruması olduğundan kod donacaktır ve işletim sistemi tarafından sonlandırılacaktır.

Gördüğünüz gibi muhteremler, sabit adresleri işaret eden işaretçileri sıklıkla kullanacağız. Şu ana kadar bellekteki bilgiyi hep int8_t olarak anlamlandırdık. Gerçekçi durumlarda genelde konfigürasyonlar mikrokontrolör register’larında struct gibi saklanır. O sebeple struct’ı nasıl sabit bir adrese gösteririz onu düşünmekte faydalar var.

Benzer bir mevzu ama, registeri doğrudan işaret eden bir etiket de aşağıdaki gibi tanımlanabilir:

 

Fonksiyon İşaretçileri (Function Pointers)

Fonksiyon işaretçilerinin bir örneğini aslında karar yapıları yazısında vermiştim ama pek açıklamamıştım. Şimdi bu vesileyle bu önemli konuyu da bir nebze daha pekiştirme fırsatı bulmuş olacağız.

C dilinde fonksiyonlar da esasen bir nevi değişkendir. Ancak fonksiyonlar, daha önceden de detaylıca açıkladığımız üzere biraz pahalı değişkenlerdir. Bu vesileyle bedeli ödenerek tanımlanmış fonksiyonların, ikinci aşamada işaret edilmesi ihtiyacı doğmaktadır. Hemen örneğimizi hatırlayalım.

Örneğimiz aslında ballı kaymak. Neden? Çünkü fonksiyon işaretçisini tanımlamak ile yetinmemişiz, o özel işaretçi tipini bir de typedef ile tanımlamışız ki, ihtiyacı olan herkes bu yeni tipten faydalansın. Bir nevi hayrat yaklaşımı olmuş. Neyse… Fonksiyon işaretçisi aşağıdaki gibi tanımlanmış:

Bu yazım tipi sabit. “donusTipi (*isim) (argumanlar)” şeklinde bir kalıbımız var. Fonksiyon işaretçisi böyle tanımlanıyor. Peki buradaki hikmeti ne bu işaretçinin? Switch-case yerine bunu kullandık tamam, aynı şekilde de çalıştı ama bu mevzunun altında yatan felsefe ne? Nesneye dayalı programlamada polymorphism yani çok şekillilik dediğimiz mevzunın C dili ile gerçeklenmesi aslında bu. Yukarıdaki kodda her durummun bir durum fonksiyonu var. Bu fonksiyonun şekli şemali hepsi için ortak. Ve hangi durum olursa, o durumun fonksiyonu çağırılıyor. Her biri aynı şekilde şemalde bu fonksiyonların, hepsini aynı işaretçi işaret ediyor sırayla ama o anki durum ne ise, o an onun fonksiyonu işaret edildiğinden her seferinde ilgili durumun fonksiyonu çağırılmış oluyor. Bu da gerçek hayatın modellenmesi konusunda bizlere büyük avantajlar sunuyor.

Fonksiyon işaretçilerinin kullanım alanları elbette ki bununla sınırlı değil. Eğer bir SDK geliştiriyorsanız, muhtemelen Callback sözcüğü ile yatıp, Callback sözcüğü ile kalkacaksınız. Bu callback mevzusunda da fonksiyon işaretçileri kullanılıyor. Şimdi diyelim ki SDK geliştiriyorsunuz ve SDK’da özel bir olay olduğunda, kullanıcının size söylediği bir fonksiyonun çağırılmasını istiyorsunuz. Yani çağırılacak fonksiyonu aslında bilmiyorsunuz bile. Sadece o olay gerçekleştirildiğinde, sisteme kaydedilmiş bir callback var ise o çağırılsın istiyorsunuz. Bu işi nasıl yazarız?

Şimdi yukarıda, SDK’yı yazanlar olarak biz çiçek gibi event callback register mekanizmasını verdik. Artık MyEvent olayı geldiğinde, eğer olay bilgisi, sınır değerden büyükse, kullanıcının verdiği herhangi bir fonksiyon çağırılacak. Bu fonksiyon herhangi bir iş yapabilir, kullanıcı ne isterse! Ama içinde while(1); gibi terbiyesizliklerin olmaması lazım! Şimdi SDK’yı kullanmak isteyen, usturuplu, terbiyeli bir yazılımcı kendi fonksiyonunu nasıl bizim SDK’ya kaydeder onu yazalım.

Gördüğünüz gibi, bir fonksiyona parametre olarak başka bir fonksiyonu verdik. 🙂 Bu ibretlik olayı fonksiyon işaretçilerinin varlığına borçluyuz. Buradan indirilecek ibreti beyinlerimize indirdiğimizi düşünüyorum. Öyleyse bir sonraki konuya geçebiliriz.

Genel İşaretçi (Void Pointer, void*)

Hiç uzatmadan konuya gireceğim. C dilinde, bir nevi joker olarak kullanabileceğimiz, her yere yedirebileceğimiz, her şeye cast edebileceğimiz bir veri tipi bulunuyor. Bu veri tipi, çok açık söylüyorum void*’dır. Genel işaretçi olarak tarif edebileceğimiz bu işaretçi tipi ile, her şeyi işaret edebilirsiniz. Daha doğrusu, herhangi bir şeyi işaret edebilirsiniz. Bu da bize jenerik kodlama yapma şansı sağlıyor.

Diyelim iki veriyi toplayan bir fonksiyon yazacak olalım. Her bir veri tipi için ayrı ayrı topla fonksiyonu yazmadan nasıl yaparız bu işi? Tüm veri tipleri için olmasa da, 32 bite kadar olan standart veri tipleri için şöyle yaparız:

Gördüğünüz gibi büyük oranda jenerik bir fonksiyon yazmış olduk.  Ayrıca void işaretçisini diğer tipteki işaretçilere cast ettiğimize dikkatinizi çekerim. Bu mereti, her tipteki işaretçiye cast etmek (aktarmak) mümkün.

Dikkat
Yukarıdaki fonksiyon float veri tipi için doğru çalışmayacaktır. Float veri tipleri, derleyici tarafından IEEE754 formatına uygun olarak -özel yöntemlerle- saklandığından ve toplandığından, uint32 formatı üzerinden iki float sayının toplanması doğru sonucu vermez. Yukarıdaki kodu, profesyonel uygulamalarda kullanmayınız.

Son olarak kısa bir hatırlatma yapayım. Bir sistemin kaç bitlik olduğunu öğrenmek için:

Bence void* büyük oranda anlaşıldı. Şimdi yine her zamanki sevimli hareketlerden birini yapacağız. Bilgilerimizi harmanlayacağız. Fonksiyon işaretçileri ile genel işaretçileri birleştirdiğimizde ne kadar güçlü bir şey elde ettiğimizi düşünelim.

Yukarıdaki fonksiyon işaretçisi tipi, giriş parametresi olarak genel işaretçiyi alıyor. Yukarıdaki fonksiyon tipindeki bir fonksiyon, tip ayrımı yapmaksızın her türlü veriyi seri porttan gönderebilir. Float olsun, int olsun, char olsun, int32_t olsun, hatta ve hatta bir fonksiyonu bile seri port üzerinden gönderebilirsiniz 🙂 Ve aynı zamanda size polimorfik bir yapı sunar. Kızarmış ekmek üstünde bal kaymak gibi… Mis.. Afiyet olsun 🙂 Öyleyse devam!

Kısaltma İçin İşaretçiler

C dilinde, kompleks veri yapıları diye bir mevzu var. İlk başta şaka gibi geliyor bu ifade ama bazı veri yapıları hakikaten kompleks olabiliyor 🙂 Bir sturct içinde başka bir struct, onun içinde afedersiniz başka bir struct, onun içinde de bir işaretçi düşünelim. Bu durumda en alt seviyedeki değikene ulaşmak hakikaten bayağı zaman alacaktır. Özellikle tembel yazılımcıları, klavyede bu kadar tuşa basmaktan nefret ederler. Bu gibi durumlarda da kısaltma amaçlı olarak işaretçilerden faydalanabiliriz. Misal:

İlk bakışta komik bir kullanım alanı gibi gözükse de, işin içine girdiğimizde bu tip kullanımların yadsınamaz miktarda olduğunu görüyoruz. Belki de tembelliğin böylesi candır canandır 🙂 Tabi burada bir pointer’ı ekstradan kullanarak bellekten biraz çaldık. Değer mi değmez mi o kararı size bırakıyorum.

Diziler ve İşaretçiler

Diziler ve işaretçiler birbirlerine çok çok yakın iki kavramdır. Nihayetinde her bir dizi, aslında bir işaretçidir. Ancak bu özel işaretçiler tanımlanırken, işaret edecekleri bellek alanları önceden ayrılır.

Diziler de tıpkı diğer değişkenler gibi ilk değer ataması mevzularına tabidir. İlk değer ataması yapılmamış bir dizi tanımı aşağıdaki gibidir:

Burada yalnızca dizinin 10 elemanlı olacağı bilgisi verilmiş. yani u8TestArray değişkeninin işaret ettiği bellek adresinden başlamak üzere 10 byte bu dizinin elemanları olarak sıralanacak.

Şimdi ilk değer ataması yapılmış ancak boyut verilmemiş bir diziye bakalım.

Burada test array aslında bildiğimiz işaretçidir. Gördüğünüz üzere kendisine “Hello!” şeklindeki karakter dizisini yani  string’i atamış olduk. C dilinde string, ayrı bir tip değildir ancak karakterlerden oluşan bir dizidir. Nitekim yukarıdakinin bir benzeri şöyledir:

testArray dediğimiz şey aslında şu aşağıdaki ile aynıdır:

Çünkü dizinin ismi yani değişken adı (testArray), aslında dizinin ilk elemanının adresini taşır (&testArray[0]).

İşte bunun farkında olmakta ve bunu unutmamakta faydalar var.

Yukarıdaki örnekte verdiğimiz Hello! yazısını karakter karakter yazdırmak istesek dizinin her bir elemanını yazdırabileceğimiz gibi, pointer aritmetiği  ile de aynı işi yapabiliriz.

Struct ve İşaretçiler

Yapılar başlıklı yazımda esasen bu konuya değinmiştim ancak ufaktan tekrar etmekte fayda var diye düşünüyorum. Struct genelde kocaman bir yapıyı modellediğinden bellekte kapladığı yer ciddiye alınmalıdır. Hal-i hazırda küçücük olup ciddiye almadığınız bir struct, yazılımın bir sonraki versiyonunda büyüyebilir ve size ciddiye almak zorunda kalacağınız bazı problemler yaratabilir. O sebeple saygıyı baştan göstermekte faydalar var.

Bir struct tanımlandıktan ve onunla ilgili bilgi belleğe yazıldıktan sonra, o veriyi kullanmak için başka bir sturct yaratmak çoğu durumda anlamsız ve mantıksızdır. Onun yerine struct işaretçisi tanımlanarak, ilgili struct’ı işaret edecek şekilde değer ataması yapılmasında sayısız faideler ve feyizler vardır.

Union ve İşaretçiler

Union bildiğiniz üzere; her elemanı belleğin aynı adresini gösteren özel bir yapı. Kendisinin feyizli özelliklerini sizlerle paylaşmıştık. Ancak tanımından da anlaşılabileceği üzere kendisi aslında bir çeşit özel işaretçidir. Dolayısıyla union kullanarak yapabileceğimiz bir çok işi, çok daha çirkin şekilde doğrudan işaretçilerle de yapabiliriz. Misal:

yerine benzer işi yapan şöyle bir kod da yazabiliriz.

Ama dediğim gibi çirkin olur kem olur. Parantez manyağı olmak zorunda kalırız ve kodumuz da anlaşılmaz.

Ancak yine de olayın altında yatan felsefeyi bilmekte faideler var.

NULL İşaretçisi

Bu NULL denen nane çok meşhurdur, her yerde de kullanılır. NULL işaretçisi esasen belleğin 0 numaralı adresini gösteren bir işaretçidir. Kendisinin tipi void*’dır.

Kütüphanelerdeki (stddef.h) tanımına baktığımız zaman şunu görürürüz:

NULL dediğimiz nane işte budur. Belleğin 0 nolu bölgesini gösteren bir işaretçi. Peki neden bir işaretçinin “boş” olup olmadığını kontrol etmek için bunu kullanırız? Çünkü bir işaretçi ilk değer ataması yapılana kadar sıfır nolu bellek adresini gösterir. Dolayısıyla bu ona ilk değer ataması yapılmadığını anlamanın güzel bir yoludur.

Fonksiyonlara Argüman Olarak İşaretçiler

Fonksiyonun pahalı bir değişken olduğunu her fırsatta söylüyorum, söylemeye de devam edeceğim. Şimdi bu pahalılıkta önemli bir gider kalemi de fonksiyonların argümanlarıdır. Bir fonksiyonun argümanları belleğin özel bir bölgesine kopyalanır. Sözü uzatmadan mevzuya dalacağım. Eğer kopyalanan şey devasa bir struct ise vay halinize. Ne demiştik? Struct kopyalamak çoğu durumda mantıksızdır. Demek ki fonksiyona argüman olarak struct yerine struct işaretçisini vermekte fayda var. Bu aynı zamanda fonksiyonun kullanacağı belleğin, fonksiyonu çağıracak kişi tarafından ayrılmasına (allocate etmek) izin verdiğinden, (caller allocation prensibi) fevkalade lezzetli bir olaydır.

Bu mevzu hakkında detaylı bilgiyi ve örnek kodları BURADAN, “Struct’ların Pointer(İşaretçi) ile İmtihanı” başlıklı bölümden bulabilirsiniz.

Fonksiyonlara argüman olarak işaretçi verilmesinin bir diğer önemli kullanım alanı da, o fonksiyonun argümanını fonksiyon içinde değiştirmesine olanak vermektir. Bu özellik yadsınamaz derecede önemlidir. Her fonksiyon tek iş yapmalıdır demiştik ama bazı durumlarda bir fonksiyonun birden fazla değer döndürmesi gerekebilir. Öyleyse o değerlerin argümanlarda işaretçi olarak alınması yeterli olacaktır çünkü bir kez değişkenin adresi bilindi mi, onun içindeki veriyi manüple etmek problem olmayacaktır 🙂

Hemen kısa bir örnek vereyim:

Bu kod çalıştırıldığında çıktısı şöyle olur:

Konsol Çıktısı
a’nin degeri 55
Function1 icinde a’nin degeri 56
a’nin degeri 55
Function2 icinde a’nin degeri 56
a’nin degeri 56

——————————–
Process exited with return value 0
Press any key to continue . . .

Gördüğünüz gibi Fonksiyon1, a değişkenini fonksiyonun dışında etkili olacak şekilde değiştirememiş, ancak Fonksiyon2 değiştirebilmiştir. Bunun sebebi aslında basittir, Fonksiyon1 a değişkeninin kendisini değil, adi bir kopyasını değiştirmiştir. Nitekim a’ların adresleri yazdırılsa farklı çıkacaktır. Bu merete Japonlar bunshin diyor 😀 Bilenler bilir.

Komut Satırı Girişi

Son olarak, komut satırı argümanlarından bahsedeceğim. Bu, gömülü sistemlerde çok da önemli değil ancak bilmekte faydalar var.

Yukarıdakiler hemen hemen aynı mevzular. Siz yazdığınız kodu derleyip konsoldan çağırdığınızda ona parametre verebilirsiniz. Bunun adı komut satırı argümanlarıdır ve program çağırılırken bu bilgi programa verilebilir. Gömülü sistemlerde kodu komut satırından çağırmadığınızdan bu olay bu amaçla kullanılmaz ama başka amaçlarla kullanılabilir 🙂 Önemli olan mevzu şudur; verdiğiniz argümanlar arasındaki boşluk bulunan kelimelerdir. Bu kelimelerin sayısı argc’ye, kendileri de argv’ye işletim sistemi tarafından aktarılır.

Hemen çok sık verilen şu kodu biz de örnek olarak verelim:

Bu kodu selam.c olarak kaydedip derlediğimizi, ve çıktı olan programın adının da selam olduğunu düşünüyorum. Komut satırından programı şöyle çağıralım “selam kardes kafan cok guzelmis nerden aldin?”

Bu durumda komut satırına girilen argüman sayısı argc = 7 olacaktır. argv[0]’ın gösterdiği yerde programın adı yani selam yazacakken argv[1]’in gösterdiği yerde kardes yazacaktır. Dolayısıyla bu kodun çıktısı şöyle olur:

Konsol Çıktısı
selam
kardes
kafan
cok
guzelmis
nerden
aldin?

Sık Yapılan Hatalar

Arkadaşlar işaretçilerin biliçsiz kullanıması çok sayıda hataya yol açabilir ve bunlar gerçekten zorlu hatalar olacaktır. Ancak sizler, işaretçileri buradaki bilgilerle birlikte kullandığınızda bu sorunları zaten yaşamayacaksınız 🙂

*Misal aşağıdaki gibi bir kod yazmayacağınızı umuyorum:

*Bir diğer hata da ilk değer atanmamış bir işaretçinin göstermediği belleğin içine bir şeyler yazmaya çalışmaktır. Kemdir, puan götürür, yapmayınız. Misal:

Daha ptr’nin nereyi gösterdiğini ayarlamadan, bu belleğin içine m’nin değerini yazmaya çalışıyoruz. Önce bu ptr bir yeri göstermeli. Misal:

*Diğer bir hata da, bir işaretçinin, ilk değer atanmamış bir değişkeni işaret etmeye çalışmasıdır. Bu şık bir kullanım değildir. Önce işaret edilecek değişkene ilk değer atamakta faydalar vardır.

*İki işaretçiyi karşılaştırmak da iyi bir fikir sayılmaz. İşaretçiler, bellekteki rastgele alanları göstereceğinden, işaretçileri doğrudan karşılaştırmak anlamsızdır. Anlamlı olabilecek şey, bunların gösterdiği değerleri karşılaştırmaktır.

 

Daha bunu sıralamakla bitmez ama siz anladınız mevzuyu.

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

Şimdi devam…

Önceki Sayfa   Sonraki Sayfa

Gömülü C – 13 : İşaretçiler

Tekrardan merhabalar. Bu gün C dilinde pointer olarak adlandırılan konu üzerine bir yazı yazmaya çalışacağım. Yine diğer yazılarda ufaktan da olsa bahsettiğim işaretçiler(pointers) hakkında fark yaratabileceğini  düşündüğüm bilgileri paylaşmaya çalışacağım.

Epeyce bir süredir, ne yazık ki yeni bir yazı yayımlayamadım. Ülkemizde yaşanan çok çok üzücü olaylar, hayatın dönem dönem hepimizi vuran yoğunluğu gibi sebeplerle sessiz bir dönem geçirdim. Ancak ne olursa olsun, başladığımız işleri bitirmekte fayda var. Bu vesileyle bu yazımın temasını “vicdan” olarak belirliyorum.

C dilinin belki de en zor konusudur işaretçiler (pointers). Nedendir hikmeti tam bilinmez, bir türlü açık seçik açıklanamamıştır. Benim yorumum şu; işaretçiler çoğu zaman basit kurgulanmış bir yapı olmadığından, anlaması da çok basit değil. Bu yorumuma katılan, katılmayan olabilir ancak sebeplerimi kendimce sıralayıp ardından olayı açık şekilde anlatmaya çalışacağım. Zira olayın aslı oldukça basit.

İşaretçiler belleğe kestirme yollardan erişmeyi sağlar diyebiliriz. İşaretçiler, türlü potansiyel sorunları beraberinde getirdiğinden bazı dillerde kullanımı yasaklanmıştır. Örneğin C#’da pointer kullanımını açmak için kodu güvenilmez (unsafe) yazdığınızı kabul etmeniz beklenmektedir. Yüksek seviyeli dillerde belleğe doğrudan erişim hakikaten güvenilmez kodlar doğurabilir ancak C gibi orta seviyeli bir dilde işaretçi kullanarak bellek erişimi yapmak, doğru tasarım kalıpları ve prensipler izlendiğinde oldukça verimli olabilir. Bizim de niyetimiz, işaretçileri doğru kullanarak C dilinin güçlü olan bu yanına hükmedebilmek bu sayede de güzel kodlar yazabilmek. Sırf işaretçiler risk unsurları yaratabilir diye, onlara kem gözlerle bakmak, onları kötü kullanmak vicdansızlık olur diye düşünüyorum.

Önemli Nokta
İşaretçilerin temel işlevi: bellekteki bir adres içindeki bir dataya erişim (* operatörü ile) ya da bir değişkenin bellekteki adresine erişim (& operatörü ile) olduğundan önce bir bellek hususunu vicdanların önüne sersek iyi olur diye düşünüyorum.

Bellek Hakkında

Bildiğiniz üzere bellek dediğimiz esasen fiziksel bir bilgi deposu oluyor. Bu zımbırtının veriyi saklama biçimine göre iki temel çeşidi var. Birincisi Volatile Memory denilen “Geçici Hafıza”. Birisi de Non-Volatile Memory(NVM) denilen “Kalıcı Hafıza”. Efendim ne göre geçici ya da kalıcı? Elektrik gittiğinde gösterdiği davranışa göre. Elektriği kessek  (ya da cihaza hard reset atsak) dahi, geri verdiğimizde veriler saklanıyorsa NVM, saklanmıyorsa VM. Misal RAM bir VM’dir yani geçici bellektir. Hard disk veya flash memory ise NVM yani kalıcı bellektir.

Pointer (İşaretçi) Nedir?

Biz işaretçiler ile hem kalıcı bellekteki hem de geçici bellekteki verilere erişebiliyoruz. Çok önemli olduğu için tekrar edeceğim. Bir işaretçi iki operatör ile ifade edilebilir.

 

Arkadaşlar C dilinde her şey bir değişken gibi değerlendirilebilir demiştik. Pointer da benzer muameleye tabi olduğundan, onun da bir tipi vardır. Örneğin

yukarıdaki dataPtr’nin tipi uint8_t* olduğundan, bu işaretçi gösterdiği bellek alanındaki bilgiyi uint8_t olarak yorumlayacaktır. Benzer bir yorum farklı veri tipleri için de yapılabilir. Misal float* tipindeki bir değişken, göstereceği adres içindeki veriyi float olarak yorumlayacaktır. Bu bilgiyi daha detaylıca açıklayacağım.

İşaretçileri anlatmanın en iyi yolu örnekler üzerinden gitmektir. Öyleyse aşağıdaki örnek üzerinden başlayalım.

Bir değişken tanımlandığında, ona uygun bir bellek gözü (ya da bellek gözleri) tahsis edilir. Hangi bellek olacağı işletim sisteminin ve derleme ortamının takdirindedir. Yukarıdaki değişkenler farzı misal 0x2015 adresinden başlayarak yerleşsin.

Şimdi kodun bir yerlerinde C dilinde şunları dersek:

Bunların insan dilinde okunuşu şöyle olur: “num değişkeninin değeri 42’dir. ptr işaretçisi ise num değişkeninin adresine eşittir.”

Tanımlandığında num değişkeni 0x2014 numaralı bellek gözünde yer almaktaydı hatırlayın. Buna göre ptr=0x2014 olması gerekir çünkü num değişkeninin adresi budur. Öteyandan ptr değişkeninin adresi 0x2018 olduğundan, ptr’nin değeri bu adreste saklanır. Buna göre 0x2014 numaralı bellek gözünde 42 yazacak, 0x2018 numaralı bellek gözünde ise 0x2014 yazacaktır. Haydi azıcık zahmet edip çizelim.

 

Şimdi bu ibretlik bilgiyi sınayabileceğiniz tam bir kodu paylaşalım.

Efendim bu kodun benim bilgisayarımdaki çıktısı şöyle:

num’un adresi: 22fe4c
ptr’nin degeri: 22fe4c
num’un degeri: 42
ptr’gosterdigi deger: 42
ptr’nin adresi: 22fe40

——————————–
Process exited with return value 0
Press any key to continue . . .

  • Gördüğünüz gibi, num’un adresi hakikaten ptr’nin degerine eşit.
  • Ptr’nin gösterdigi deger hakikaten num’un degerine eşit.
  • Ve hakikaten ptr, bir degisken olarak bellekte saklaniyor ve adresi num’un adresinden farklı.

Mevzuyu açık seçik gösterdik diye düşünüyorum. Şimdi benzer bir olayı diziler üzerinde anlatalım ki başka püf noktaları görelim.

Öncelikle kodu okuyarak anlamaya çalışınız.

Şimdi bu çıktıları teker teker yorumlayalım. Ancak önce dizinin ve işaretçilerin belleğe nasıl yerleştiğini farz-ı misal bir açıkalayalım.

Bu kodun benim bilgisayarımdaki çıktısı aşağıdaki gibi oluyor.

name’in adresi: 22fe40
p8’in degeri: 22fe40
p32’nin degeri: 22fe40
p8’in gosterdigi deger: 79 [O] p32’nin gosterdigi deger: 1701280335 [65677a4f] p8’in adresi: 22fe38
p32’nin adresi: 22fe30

isaretci degerleri arttiktan sonra!!!

p8’in degeri: 22fe41
p32’nin degeri: 22fe44
p8’in gosterdigi deger: 122 [z] p32’nin gosterdigi deger: 0 [0] p8’in adresi: 22fe38
p32’nin adresi: 22fe30

——————————–
Process exited with return value 0
Press any key to continue . . .

Şimdi bu çıktıları tek tek yorumlayıp ibretleri beynimize indirelim.

“name” ile tanımlanan dizinin başlangıç adresini benim bilgisayarım 0x22fe40 olarak atamış. p8 ve p32 işaretçilerine name dizisinin başlangıç adresini atadığımdan, hakikaten bu iki isaretcinin degeri name dizisinin baslangıç adresi yani 0x22fe40 olmuş. Buraya kadar güzel, zaten konuştuğumuz şeyler.

p8’in gösterdiği değer ‘O’ karakteri yani 79 sayısı olmuş. Bu normal çünkü p8’in değeri zaten name dizisinin başlangıç adresi idi ve o adresin içindeki verinin int8 olarak anlamlandırılması hakikaten ‘O’ karakteri.

p32’nin durumu biraz daha karmaşık duruyor ama aslında olay çok basit. Big endian little endian olayını hatırlarsınız. Ascii table konusunu da konuşmuştuk onu da aklımızın bir köşesinde tutalım. Ve sihirli sayıyı analiz edelim. 0x65677a4f. 0x4F aslında ‘O’ karakteri [bkz: ASCII table]. 0x7a=’z’, 0x67=’g’, 0x65=’e’. Aaaa, aslında bu bizim name dizisindeki ismin int32_t olarak yorumlanmış hali. İşte işaretçinin tipi burada devreye giriyor. Aynı bellekteki bilgi int32_t olarak (yani 4 byte) işaret ediliyor. Her şey şimdi aydınlandı.

p8++ dediğimizde basitçe p8’in değeri kendi cinsinden artırılıyor. Peki  p8 neye eşitti? name dizisinin başlangıç adresine. int8_t tipi 1 byte olduğundan bu p8’in yeni değeri name dizisinin başlangıç adresinin 1 fazlası oldu.

p32++ dediğimizde basitçe p8’in değeri kendi cinsinden artırılıyor. Peki  p32 neye eşitti? name dizisinin başlangıç adresine. int32_t tipi 4 byte olduğundan bu p32’nin yeni değeri name dizisinin başlangıç adresinin 4 fazlası oldu.

Gerisini yorumlamayı size bırakıyorum. Eksik bir nokta olursa çekinmeden sorabilirsiniz, paylaşabilirsiniz.

Bu arada & ile * birbirlerinin tersidir farketmişsinizdir. Örneğin

 

dediğimiz nane aslında p8’nin kendisidir çünkü * ile & birbirini götürür. Yine

 

nanesi de p8’in kendisidir aynı sebepten.

Sizlere nacizane tavsiyem işaretçilerle mümkün mertebe oynamanız.  İşaretçilerin temeli budur. Bir dahaki yazıda uç noktalara değineceğiz.

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

Şimdi devam…

Önceki Sayfa   Sonraki Sayfa

 

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

 

Gömülü C – 11 : Döngüler

Selamlar efendim. Bugünkü yazımızın naçizane konusu, C dilindeki döngü yapıları olacak. Kontrolsüz dönüşler mide bulandırır malumunuz. İşbu döngüleri nasıl yapar, nasıl ederiz de dönme dolap misali zevk verir hale getiririz bunu dilin döndüğünce anlatmaya çalışacağım. Nasıl anlatacağım? Örnekler ile efendim.

Bir kaç basit kural ile, olayın özünü aktarmaya çalışacağım. Meselenin aslı ortaya kondu mu, çözüm bulmak kolay olur demişler 🙂

Döngülerin Sabit Limitler İle Sınırlandırılması

Efendim malumunuz, ağzımızdan çıkan iki kelimeden biri “sınırlı” sözcüğü. Gömülü sistemler şöyle sınırlı, böyle sınırlı deyip duruyoruz. Madem her-şey böyle sınırlı, sonlu sürede bitmesini istediğimiz işler için de bir sınır koymamız şart 🙂

Bir kere döngü dediğimiz nanenin, statik olarak öngörülebilir, sabit bir üst limitinin olmasında sayısız faydalar var. Bunu sadece ben demiyorum, kodunuzu hangi statik kod analiz programına verseniz, aynısını size o da söyleyecek. Yalnızca onun benim kadar nezaketle söylemeyeceğini bildirmek isterim.

Peki her döngü ille sınırsız mı olacak? Bir sınır konulanlar evet. Ama task tanımlamalarında vs yapılan bazı döngülerin özellikle (sözde) sonsuz olması gerekir. O zaman da ben bunu bilinçli yaptım, ne yaptığımın da farkındayım diye bu döngünün tepesine ecnebice;

ya da kendi dilimizde;

gibisinden birşeyler yazmakta fayda var.

Muhteremler, tamam peki sınırlı döngü olacaksa sınır olur zaten dediğinizi duyuyorum. Önemli nokta bu sınırın, deterministik ve sabit bir sınır olması. Hemen mevzuyu yine örnekle açıklayayım.

Kötü Örnek:

Mesela bu yukarıdaki döngü ne zaman biter? Mantıken bitmez aslında ama, pratikte indexLimit değeri taşıp sıfırlanıp index değerine tur bindirince bir yerlerde belki biter. Bu durum oldukça çirkin bir durumdur ve genelde yok yere hem uygulamanın kompleksitesini doğrusal olmayan yani tahmin edilmesi güç noktalara getirebilir, hem de gereksiz yere çokça güç tüketimine sebep olur. Mazallah daha fena hadiselere de sebep olabilir ama onları söylemeye dilim varmıyor.

Peki ne yaparız? Bir önceki yazıdan kopyayı çekiyoruz.

İyi Örnek:

Yine imdadımıza define yetişti. Bizim dayının kulakları çınlasın. Artık döngünün, çiçek gibi bir limiti var. Hem ne olduğu hem ne olacağı gayet anlaşılır. Kodu makine için değil insan için yazdığımızı hatırlayacak olursak, konsepti anlatan kodlar yazmanın ehemmiyetini idrak etmemiz kaçınılmaz olur.

Erişilebilir Döngü Sınırı

Şimdi diyelim sınır, öngörülebilir bir sınır. Ama arada mayınlı bölge var ulaşmak mümkün değil. Böyle sınır, ancak hasret ve hüzün doğurur. Döngüyü kontrol eden sayacın, döngü sınırına varabilmesi gerekir. Bazen farketmeden, erişilemez döngü sınırları koyuyoruz. Bundan çekinmek ve tiksinmek lazım.

Misal üzerinden gidelim yine ki mevzu havada kalmasın, ayakta kalmasın, yerine otursun. Diyelim döngüyü kontrol eden sayaç uint8_t cinsinden ve döngünün sınırı da 2000. Bu durumda aklı başında herkes bir durur ve der ki, aga bu nedir? Niye böyle der? Çünkü uint8_t tipinde birisi asla ve asla 2000 değerine ulaşamaz. İstemsiz olarak yaratılmış bir sonsuz döngüye hoşgeldin diyebilirsiniz. Hatta hoş geldin faslından sonra çay kahve de ikram edebilirsiniz çünkü döngüden asla çıkılmayacağından her şey için yeterince zaman olacaktır. Tabi bu durumda döngünün dışını yani özgürlüğü unutmak gerek. Neyse, demek ki neymiş; erişilemeyecek döngü sınırı kötüymüş.

Kötü Örnek:

Çok söze gerek yok. Peki iyisi nasıl olur bunun? 1000’e kadar sayacaksanız ona göre bir sayaç tipi seçmekte fayda var. uint16_t olabilir mesela ama 2 byte sıkıntınız yoksa şayet, ben gelecekteki değişikliklere de pay bırakmak için uint32_t tanımlardım.

İyi Örnek:

İyi örnekte, sayaç tipini uint32_t tanımlayarak, uzakları yakın ettik 😀 Artık döngümüz ulaşılabilir bir sınıra sahip!

Döngünün Namusu

Biraz ağır olmuş olabilir başlık ama, döngünün kodda da bir sınırı olmalı.

Önemli Nokta
Süslü parantezler döngünün sınırıdır. Sınır namustur demişler. Bu nedenle hataların önüne geçmek için döngüleri de lütfen sınırsız, parantezsiz kullanmayınız. Tekrar söylüyorum, zaten hata çıkarabilecek tonla şey varken, bir tane daha eklememizin hiç gereği yok.  

Hemen hızlıca bu hususta da kötü örneği ve iyi örneği hızlıca verelim ve bekleme yapmaksızın devam edelim.

Kötü Örnek:

İyi Örnek:

Demek ki neymiş, süslü parantez yine canmış cananmış 🙂

Şeytani Sözcük: goto

Arkadaşlar bu goto belasını, dış mihraklar başımıza sarmıştır. Sırf kodlarımız hatalı olsun, anlaşılmasın, gelişemeyelim diye önümüze konmuş şeytani bir sözcüktür bu. Kullanılmamasının sevpo kazandırdığı, sayısız makale ile sabitlenmiştir. Tek söyleyeceğim: yapmayın, etmeyin.

Hiç elim gitmiyor goto olan bir kodu yazmaya ama kötü örnek olarak yine yazacağım.

Kötü Örnek:

İyi Örnek:

goto belasını kullanmadan da, emin olun ki yapmayı istediğiniz şeyi yapmak mümkün. Dolayısıyla çirkinleşmemek adına, goto kullanmayalım.

Rastgele Değişken Tanımlama

Şimdi bu başlık “rastgele değişken tanımlamak” diye anlaşılmasın, başlık direk yapmamanız gereken bir şeyi açıkça anlatmak için böyle konulmuştur. Önerimiz basit; “rastgele değişken tanımlama dostum”. Ne demek istiyoruz burada, esasen döngünün içinde değişken tanımalamasınız iyi olur demek istiyoruz.

Gömülü C derleyicilerinin hepsi C99 değildir. Bazısı yalnızca ANSI C’ye izin verir. Dolayısıyla yazdığınız kod her yerde derlensin istiyorsanız, döngü içinde değişken tanımlama lüksünden vazgeçiniz.

Kötü Örnek:

İyi Örnek:

Yakışıklı While

Hep for üzerinden gittik. Bunlar aslında while için de geçerli. Ama while için de yakışıklı  görülen bir şablonu sizlerle paylaşmak isterim. Ama önce artizlik belirtisi olarak yazılan kötü bir örneği sizlerle paylaşacağım.

Kötü Örnek:

Burada yazar, “true” yani dogru ifadesini yakalamak için !0 yapmış. !0 ifadesi her zaman yanlışın değili, yani doğru olacağından, while burada sonsuz döngü olmuş olur. Ancaaaaak, döngünün her bir dönüşünün başında, sıfır değeri 1 kere NOT işlemine tabi tutulur. Bu da her döngüde 1 CYCLE yani 1 saat darbesi kadar iş gücünü çöpe atmaktır. Yani bu sonsuz süreli bir israftır. Böyle bir  hatanın tarifi olmaz dostlar, yapmayın.

İyi Örnek:

İyi örnek çiçek çiçek. Söylenecek söz yok 🙂

Eveeet, bir programın daha sonuna geldik. Umarım sıkılmadan okumuşsunuzdur.

Unutmadan; yazıları beğendiyseniz eğer,  faydalanabilecek arkadaşlarınızla da paylaşabilirseniz sevinirim.

Şimdi devam…

Önceki Sayfa   Sonraki Sayfa

 

Gömülü C – 10 : Gömülü C: Etiketler ve Makrolar

Bu günkü yazımda, diğer yazılarda da ucundan kulağında bir miktar bahsetmiş olduğum #define ile tanımlanan etiketler üzerinde duracağım. Her durumda etiketleme yapmak doğru olmayabilir, ancak C dilinde etiketler çok  önemlidir. Bu sebeple, çiçek gibi kodlama yapmak için bu müstesna özelliği ne şekilde kullanmak lazım ona değineceğim.

Bunları ifade ederken, dayıyla olan anılarımızdan dem vuracağım. Yani bu yazının teması bizim dayı.

Etiketler diye adlandırdığım bu #define nanesi genel olarak iki temel amaca hizmet eder efem: etiketleme amacı ve fonksiyonel amaç. Her iki amacın da altına yatan nane aynı define nanesi olduğun için, nanenin iki çeşit sıkılmış suyu da özünde aynı tadı verir. Bu tadın güzel olması için bir iki püf noktasını aklımızda tutmamızda sayısız faideler var. İşbu yazıda bu püf noktalara dem vurulacaktır.

Etiketleme Amacıyla Kullanımı

Bu kullanımı esasen magic number coding kısmında anlatmıştım ama şimdi biraz daha farklı bir açıyla konuyu felsefi olarak irdeleyeceğim. #define ifadesinin etiketleme amacıyla kullanılması esasen, verinin bilgiye dönüştürülmesi yani bir anlamda verinin bilgi olarak etiketlenmesidir.

Hatırlayın, ‘A’ karakteri de, emeklilik yaşı da 65 sayısı ile ifade ediliyordu. Yani burada iki farklı bilgi, aynı veriyle ifade edilebiliyordu. O zaman hangi verinin hangi bilgiyi etiketlediğini bilebilmek için basitçe onu etiketlememiz gerekiyor 🙂

Kötü örnekten örnek olmaz ama, misal diye söze başlayarak bir kötü örnekle devam edelim.

Kötü Örnek:

Şimdi bu kodu yazan dayı bir nane yemiş yazmış, belli ki de çalışıyor diye sonradan da bozmamış. Hadi dayı bu naneyi yemiş, kimse de çıkıp dememiş ki “Aga bu nedir?”. Ben gittim sordum, kodu yazan dayının amacı, bir işçi eğer 65 yaşından büyükse, o ilgili şirkette A sınıfı bir çalışan oluyormuş. Yazılım da şirket çalışanlarını bu anlamda kontrol edip etiketliyormuş. Dayı dedim bak bizim blogda böyle böyle mevzular var, bunun adı magic number coding. Hadi biz gördük sineye attık, el-alem görse bu koda ne der? Düzeltelim öyleyse dedi dayı, neye düzelttiğini siz biliyorsunuz :).

İyi Örnek:

Neyse çıktım sonra dayının yanından, yürüyorum. Bi baktım arkadan biri “hooooop” diye bağırıyor. Döndüm bir baktım yine bizim dayı. Dayı bir disketi elime tutuşturdu. Al dedi yeğen, lazım olur. Neyse güç bela disket okuyuculu bir bilgisayar buldum ve içindeki .c uzantılı dosyayı gördüm. Heyecanla dosyayı açtığımda, olan biteni hemen anladım. Çünkü dayı kodu düzeltmiş, alemlere ibret olsun diye diske koymuş. Yazılımı okurken sanki şiir okur gibi, her bir şeyi anladım. Eşşeği bağlasak o da anlardı, kod öyle sanatsal yazılmıştı. Hele de  AGE_TO_BE_RETIRED ile WORKER_CLASS_A’nın aynı değere sahip olup başka mevzuları anlattığını görünce, gözümün önünden nef-i üstadın şiirleri geçti. Kafiye misali iki şey aynı veriyle ifade edilmiş ama bambaşka bilgileri taşıyordu. Derin bi nefes aldım ve içinden dedim ki dayı helal olsun.

Fonksiyonel Amaçlı Kullanım

Malumunuz fonksiyon, çok güzel ama pahalı bir olay. Bir fonksiyon tanımlayınca yok stack idi yok parametrelerin kopyalanması idi yok v tablosuydu çok masraf oluyor. Dertlendim bir gün, gittim dayıya. Dayı dedim enflasyon olmuş bilmem kaç? Bu fonksiyon çok pahalı dedim. Hep kambur sandığım omurgasını doğrulttu ve uzaklara bakarak dedi ki: “doğrudur yeğen, ama her derdin bir ilacı var”. Nedir dedim dayı, derdime derman. Gel beri bre çoban dedi, gel.

Önce dedi, yol yordam. #define var dedi bizim, ama fonksiyon yerine kullanacaksan, hor kullanmayacaksın, ne yaptığını bileceksin dedi. Nasıl yani dedim? Önce dedi kötüsünü göstereyim.

Kötü Örnek:

Dedim dayı bu kod bizim blogda vardı. Yeğen dedi, hakikat hep gözümüzün önünde değil midir? Tamam dedim dayı dinliyorum. Bak bu kod dedi, şuna çevrilir.

Bu da dedi, kemdir, hiç bir derleyici bunu kabul etmez. El-aleme rezil rüsva olmak var. Ama bunun da bir çözümü var, dedi. Dedim dayıcım bak yazmıştık bunu, dur dedi. Tamam dedim.

İyi Örnek:

Bak dedi, bunu böyle yazarsan her türlü çalışır. Su gibi, çiçek gibi olur. Mis kokularla akar gider dedi. Dedim dayıcım… Biliyorum dedi, ama bir kez daha hatırlatmakta sayısız feyizler var dedi.

Dedim dayıcım yeni numaralar var mı? Var dedi, beni takip et. Neyse bir alışveriş merkezine girdik. Dedi burada sana get-set kullanımı ile ilgili önemli bir ilim aktaracağım. Bak dedi #define’ın get amaçlı kullanımı çok tehlikelidir. Sen hataları baştan önlemezsen, hatalar gelir seni bulur. Misal dedi:

Sıkıntılı Örnek:

Bak yeğen dedi dayı. Yıkarıdaki GET makrosunu sen yazmıştın ama dış güçler, senin yazdığın makroyu aşağıdaki gibi haince kullanarak, makinemizin sıcaklığı ile oynadılar. Makinelerin garantisinin bittiği gün hepsini yaktılar. Oldu mu dedi şimdi?

Dedim dayı ne diyorsun? Şaka şaka dedi, ama dedi bak önlemini şöyle al.

Sıkıntısız Örnek:

 

Koda baktım, dedim ne çakallık, pes doğrusu. Dayı, değişkeni get ederken başına koduğu const ile, değişkeni read only yani sadece okunur yapmıştı. Dayı dedim bu da bana ders olsun, senden korkulur. Ne sandın yeğen dedi.

Karakter Savaşları

Neyse çıkışta dayı tutturdu tarih anlatıcam diye. Ne tarihi? Savaş tarihi. Kimin savaşı? Karakterlerin. Haydi hayırlısı dedim. Dayı dedi ki, derleyici de sonuçta insan işi. İradesi küll-i. Çok karıştırırsan kafasını hata yapar dedi. Ben kahkahayı patlatınca, açtı kodu pat diye.

Kötü Örnek:

Bak dedi yeğen, bu makroda hangi c, makrodan alınacak belli olmadığından, her derleyici buna aynı cevabı vermez. Kafası karışır biri c karakteriyle girdiğin sayıyı toplar biri başka iş yapar dedi. Gözlerimi faltaşı gibi açıp, ibreti download etmeye başladım. Download işi biter bitmez dayı bir de bu kodu incele dedi.

İyi örnek:

Bak dedi yeğen, bu tanımda karakter savaşları olmaz. Herkesin yeri belli, burada sulh tahsis edilmiş olur dedi. Bu ibreti de download ederek olay yerinden usulca uzaklaştım.

Dayıyla o günlük muhabbetimiz burada tamamlandı. Hepinize selamı var 🙂

Unutmadan; yazıları beğendiyseniz eğer,  faydalanabilecek arkadaşlarınızla da paylaşabilirseniz sevinirim.

Şimdi devam…

Önceki Sayfa   Sonraki Sayfa

Gömülü C – 9 : Gömülü C: Koşullamalar 2

Koşullamalar yazısında if-else yapıları ile ilgili söylenebilecek güzel bir takım şeyleri, lisan-ı münasiple konuşmuştuk. O yazı çok tutunca, devamını çekmek farz oldu.

Bu arada dostlar, yazılarımdaki temalarla ilgili yorumlarda insanların ağzından bal damlıyor. Bu vesileyle bu yazının teması bal oluyor 🙂

Malumunuz if-else yapısı, dertlere derman, kararsızlığa düşman. İşin şakası, verilecek bir kararı olan herkesi if-else kullanmaya davet etsek yeri midir diye düşünmüyor değilim. Bir karar verilecekse hiç düşünmeden if-else kullanırdım. Ama biraz düşünsem, her yerde if-else kullanmazdım 🙂 İşte bu yazının özü de bu olacak. Haydi bakalım, koşullamaları sıksak içinden ne çıkacak!

 

1. if-else Nedir, Nerelerde Kullanılır

Bir önceki yazımızda, if-else koşullamalarının nasıl kullanılması gerektiğine ve nasıl kullanılmaması gerektiğine haddince, hatta belki haddinden bir tutam fazla değindik. Şimdi tutup da, onca felsefi ve uhrevi bilginin ardından tekrar bu kadar temel sorularla karşınıza çıkınca, ben de garip hissetmiyor değilim. Haydi bakalım hayırlısı.

Sevgili okuyucu, if – else yapıları adı üzerinde eğer öyleyse şöyle yap, eğer böyleyse böyle yap türünde olan, dolayısıyla yapıyı anlamak için bütün koşul bloğunu anlamamızı, okumamızı gerektiren yapılardır. Bu yapıları okumak, sinemada film izlemeye benzer. Başı çok anlamsız olan bir film sonunda kendini övdürebilir, başta güzel giden film sonunda sövdürebilir, kimisiyse insana kendini dövdürebilir. Bir başka örnek ile anlatmak gerekirse, ileriden gelen uçan böceğin, afedersiniz eşek arısı mı bal arısı mı olduğu anlamak için yaklaşmasını beklemek zorunda kalmaya benzer bu süreç.  Ve her bir koşulun geleceği, her an başka bir koşul tarafından bertaraf edilebilir. Bu da çoğu zaman sirke tadı verir. Bu ibretlik tespiti, lütfen aklınızdan çıkarmayınız. if-else’de her bir koşulun kaderi, diğer koşullara çok çok bağlıdır.

Mesela asagıdaki kodda, 18 yasindan kucuk arac kullananlara ceza kesilmiş. Yani 18 yaş ve üzerini göndermiş polis amcalar.

Öyle ya, burası Türkiye. Bir torba yasa ile bu kanun değişti diyelim. 20 yaşından küçüklere stajer sürücülük süresi var. Bu süreçte de tek kullanmak yasaklandı. Yukarıdaki kodu yazan arkadaş ister istemez sabah gidip kodu güncelleyecek. Ne yazsa beğenirsin…

Adam kodu çiçek gibi yazmış, koşulunu araya eklemiş. Ama en alttaki else artık bizim bildiğimiz else değil. Biz çam balı diye aldık, reçel çıktı. Önceden else koşulu 18> yaşların tamamını desteklerken, bir gece uyuduk uyandık bir baktık ki, tek satırını değiştirmediğimiz else bloğu gitmiş yerine 20> yaş için geçerli else bloğu gelmiş. Tek satırını bile değiştirmediğiniz bir kod alanının, kimyasındaki bu değişiklik, hepimizi ziyadesiyle paranoyak eder artık kod yazarken 🙂

Aynı zamanda koşullar çok uzun olursa, yani; “öyle olursa şöyle yap ama öyle olmaz da böyle olursa, ya da şöyle olmazsa böyle yap” filan gibi bitmek bilmeyen, gelişimini çoktan tamamlamış, dünyanın en akıllı ırklarından olan bir insanın dahi beyninin tek hamlede saklamayacağı koşullamaları bu yapılarla tutmamakta fayda var. Zira her bir koşul, ister istemez diğerini etkiler ve koşul sayısı arttıkça halis bildiğimiz balın dibinde şeker tabakası birikmeye başlar. O bal da kaymakla bile tad vermez. Bu gibi nedenlerledir ki, uzun if-else blokları da çok can sıkar, çok can yakar muhteremler. Tıpkı kullanmasını bilmeden kullanmaya kalktığımız her şey gibi, bunun da sonu mutlak hüsran olur. Çözüm, gereksiz uzun if-else’leri traşlamaktan geçiyor. Aşırı uçları temizleyelim. Bunu yapmanın en iyi yolu, durum indirgemesi yapmak. Nasıl yapacağız, misal Karnaugh ile yapacağız. Durum indirgemelerini dostlar, lojik yazısnda aktarmıştım, tekrara düşmemek adına burada artık söz edip geçiyorum.

Ama if-else’in çok güzel bir yanı da vardır ki, bu yapı bize, aralık ölçeklemesi yapma şansı verir. Yani bu kovanda 100’den az arı varsa, ben bu bala çökerim önermesini, çok net if-else ile yapmak mümkündür hatta mübahtır.  Demek ki aralıklar ile bir ölçekleme yapacaksak, if-else kullanmakta faydalar varmış. Ehliyet kontrolü yapacağız diye tüm yaşları ayrı ayrı analiz etmedik misal.

Peki bu if’in onca zaafiyeti var biz napalım kullanmayalım mı bunu? Hayır efendim, nerede nasıl kullanacağımızı anladık. Çiçek balı gibi yapılar kurulabilir if-else ile. Ama unutmayın ki, tek bal çeşidi çiçek balı değildir. Pekala da çam balı da vardır!

2. switch-case Nedir, Nerelerde Kullanılır

İşte bizim çam balı: switch-case. Seç beğen al usulu hem de, tam isabet sevenlere. if-else için koşulların geleceği birbirine çok bağlı demiştik, bu amansız derdin dermanı switch case yapısı. Zira switch case’de her seferinde (aslında genelde), durumlardan biri çalıştırılır. Ve yine doğru kullanımda, koşullar yani durumlar birbirlerini etkilemez. Bu nedenledir ki, durum makinaları   switch case ile gerçeklenir. Öyleyse ibret yükleme moduna geçiyoruz: switch case nokta atışı için, if-else ise aralık ölçeklemesi için kullanıldığında çiçek olur. Bu vesiyleyse if kardeşimizin de iade-i itibarını yapmış bulunduk.

Şimdi switch case ile durum analizine bir örnek verelim.

Yukarıdaki yapı çiçek gibi bir durum makinesi olaraktan tariflenebilir. Gördüğünüz her bir case, bir ve yalnızca bir duruma karşılık düştüğünden, if’deki karmaşa burada asla yaşanmaz. Ayrıca enum ile switch-case’in bal kaymak misali bir uyumu vardır. İkisinde de araya birşeyler eklemeniz, diğerlerinin keyfini hiç mi hiç bozmaz. Bu söylediğimi, enum’u anlatacağım yazıda daha güzel aktarabileceğim çünkü bulmacanın parçaları bir araya gelmiş olacak.

Switch case uzun dahi olsa, her bir durum kendi içinde bir cumhuriyet olduğundan, okuması hiç zor olmaz. Ancak tabi ki unutmamak gerekir ki if’lerde olduğu gibi,  iç içe çok sayıda switch yapısı kullanmak, ortaçağda insanlara uygulanan işkence çeşitlerinden birisidir. Hak yolu varken bu yollara hiç gerek yok.

Peki bu koşullamalar sadece if-else ile mi yapılır? İşte şimdi karadenizin doruklarından deli balı huzurlarınıza geliyor!

3. Koşullamasız Durum Makinası

Basit bir durum makinası düşünelim. 4 tane durum olsun. Başlangıç durumumuz 1 yani 1. durum olsun. Sistemde her zaman durum akşışı şöyle olsun: 1->3, 3->2, 2->4, 4->1. Bunu nasıl modelleriz? Misal şöyle modelleriz:

Peki bu çam balı gib halis munis yapı, daha basit, daha sanatsal bir şekilde kurulamaz mı? Switch case’i ortadan nasıl kaldırırız? if else ile diyenleri duyar gibiyim ama bu başlığın ibreti ve vizyonu biraz daha büyük. Karabüyü ile harmanladığımız deli balı ile bu yapıyı gerçekleyelim.

Yukarıdaki kodu çalıştırdığınızda konsol çıktısı şöyle oluyor:

State1
State3
State2
State4
State1
State3
State2
State4
State1
State3

Yani, durum makinamız, durumları koşulları kullanmadan gerçeklenmiş oldu. Bu kodun hikmetini anlayanlar vardır anlamayanlar vardır. Anlamayan üzülmesin çünkü işaretçiler başlığında bunları da anlatacağım. Burada OOP’dan da polymorphism’i ucundan kulağından çarpmış olduk. Bir yapısal programlama dili ile çalışmamız bunu değiştiremedi 😉

Dilerim her üç balın da tadı hoşunuza gitmiştir. Bu balları yiyen şişman yemeyen pişman 😀

Arayı fazla açmadan yazılara devam etmeye çalışacağım.

Unutmadan; yazıları beğendiyseniz eğer,  faydalanabilecek arkadaşlarınızla da paylaşabilirseniz sevinirim.

Şimdi devam…

Önceki Sayfa   Sonraki Sayfa

 

 

Gömülü C – 8 : Gömülü C: Koşullamalar

Koşullamalar bildiğiniz üzere programın akışını kontrol eder. Şu dünyada en güzel akışkan su olduğundan, bu yazının teması su olacak.

KOŞULLAMALARIN DÜZGÜN KULLANILMASI

Su akar yolunu bulur demişler. Güzel kod da bu misal. İyi yazılım akışı, iyi koşullamaya çok bağlıdır. Koşullamalar düzgün yapılmadıysa, kodda mutlaka birtakın hatalar çıkacak, o kodun ne yapacağını Laplace Şeytanı dışında kimse hesaplayamayacaktır. Şimdi ilk okul yıllarımızdan tanıdığımız parantez ile başlayalım.

1. Parantez

Muhterem dostum, koşullamalar mutlaka ama mutlaka çengelli parantez ile kullanılmalıdır. Parantezsiz if-else yapmak da mümkündür ama o kod su gibi akmaz, viskozitesi yüksek olur, katranlaşır. Bir kodun genişletilebilirliği, geliştirilebilirliği, parantez kullanılmadığında çok azalır. Ayrıca hata çıkma olasılığı da artar.

Kötü Örnek:

Şimdi bu kötü örnekte acaba kodu yazan kişi, USB’ye bağlanıldığında iki iş de yapılsın istemiş de hatalı mı yazmış, yoksa birinci fonksiyon koşula bağlı, diğeri her türlü yapılsın diye mi yazılmış belli değil. Belki de ilk aşamada koşulun içinde yalnızca bir fonksiyon vardı. Kodu güncelleyen kişi, “aaa bu da yapılsın” diyerek diğer satırı eklediğinde bir felaketle karşılaşacaktır.

İyi Örnek:

Su gibi kod. İki tanecik parantezle, koşulun sınırları çizildi, akan su yolunu bulmuş oldu.

2. Karşılaştırma Sırası

Hep söylüyorum, söylemeye de devam edeceğim ki, koşullamalarda en sık yapılan hatalardan biri de == yerine = yazmaktır. Bunun çözümü, yine söylüyorum basittir. Diyelim ki, beşer şaşar misali kodu yazarken bir hata yaptık if(deger == 3) diyecekken, if(deger = 3) dedik. Bu kod hata vermez ne yazık ki ve degere 3 atanır, sular sel olur evleri barkları yıkar. Ama alışkanlığımız if(3 == deger) yazmak ise, hata yapıp if(3=deger) yazsak bile derleyici bize hata verir. Bu da ev bark kurtarır. Tabi burada 3 sayısını da kullanmamak lazım. Magic number coding konusundan bahsetmiştik. Şimdi örnekleri verelim…

Kötü Örnek:

 İyi Örnek:

 3. Koşul Derinliği

Çok derin olan her mevzu gibi, derin koşullamalar da kötü olaylara delalet eder. İnsan beyni ortalama 7 derinliğe kadar ilişki saklayabiliyormuş, bundan fazlası kafa karıştırıyor ne yazık ki. Suyu bulandırmanın anlamı yok, böyle bir ilişki varsa bile olayı basitleştirmeye çalışmakta faideler var.

Kötü Örnek:

 İyi Örnek:

 4. Koşullamalarda Makro Kullanımı

Makrolarla koşullamaları yanyana kullanmak ateşle barutu yan yana tutmaya benzer. Çok çok dikkatli olmak gerekir. Makrolar’ın do, while ya da benzer bir yapıda kullanılmasında faideler vardır. Lafı çok uzatmadan iki örnekle açıklayacağım.

Kötü Örnek:

Bu kod, derleyici tarafından aşağıdaki şekilde genişletilir:

Ki bu yazım zaten dilin tabiatına yani doğanın kanunlarına aykırıdır. Bu durumda, görülen else, artık if’e ait değildir. Bu da öngörülemez felaketlere sebep olabilir. If’de parantez kullanılmaması da hatalara çanak tutmuştur.

İyi Örnek:

Yukarıdaki kod ise, parantezler konmasa bile çalışacaktır. Kodu genişlettiğimizde hiç bir hata oluşamayacağını görmek mümkündür. Fonksiyonel makroların, do while içinde tanımlanması, su gibi aziz bir alışkanlıktır.

 5. Parametre Dönüşümü

Bazen koşul, ister istemez karmaşık olur ve çok sayıda koşulun hikmetini çözmek hakikaten zordur. Bu durumda parametre dönüşümü yapmakta sayısız feyizler vardır. Bu parametreler toplanarak anlamlı tek bir parametreye aktarılabilmektedir.

Kötü Örnek:

 İyi Örnek:

Artık o kadar koşulun ne işin gerektiğini anlayabiliyoruz. Adam araba sürebilir mi süremez mi basitçe buna karar veriliyor.

 Burada anlaşılabilirlik uğruna bir byte’ın kurban edildiğinin bilincinde olmakta fayda var. Değer mi? Bence değer. 

Şimdi devam!

Önceki Sayfa   Sonraki Sayfa