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? Şöyle yaparız.

Gördüğünüz gibi 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.

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

9 thoughts on “Gömülü C – 14 : İşaretçiler 2

  1. ali

    Hocam şu yapıyı biraz daha açabilir misiniz ?

    typedef void (*myStateHandler) (void);

    Pointer fonsiyon prototipi mi tanımlanıyor burada??
    typedef ne işe yarıyor burada (normalde mantığını biliyorum ama burada garip geldi.)

    1. Gösterdiğiniz veri tipi tanımı, myStateHandler isminde bir fonksiyon işaretçisi (function pointer) tanımlamaya yarar. Burada tanımlamış olduğumuz myStateHandler ismindeki yeni veri tipinin, bir fonksiyon prototipi tanımlayan bir işaretçi olduğunu söyleyebiliriz. Buradaki typedef ifadesi, bu fonksiyon tipinin; yeni bir veri tipi olduğunu belirtmek için. Fonksiyon işaretçileri C dilinin en önemli konularından biridir. Yazıdaki örnekler üzerinde pratik yaparsanız, konunun mantığını tam olarak oturtmanız daha da kolaylaşır diye düşünüyorum.

  2. ali

    hocam şurada hata var mı :
    uint32_t nRead;
    uint32_t index;
    typedef union {
    uint32_t u32Data;
    int16_t as16Data[2];
    int8_t as8Data[4];
    }tu_DataGroup32;

    tu_DataGroup32 UData;
    // ( Code to read in nRead, from the user or a file, has been omitted in this example )
    UData.u32Data = nRead;
    for( index= 0; index < sizeof(uint32_t); index++ )
    {
    printf( "Byte number %d of %ud is %udn", index, nRead, UData.int8_t[index] );
    }

    UData.int8_t[index] ifadesindeki int8_t yerine as8Data [index] mi olacak ?

  3. aykut

    Hocam merhabalar. Öncelikle hazırlamış olduğunuz yazı dizisi gerçekten çok güzel ve eğlenceli. Burada öğrendiklerimi pekiştirmek isterken bir hata ile karşılaştım. Function Pointers başlığı altıntaki ilk örneği olduğu gibi tek bir main.c veya main.cpp dosyasına yapıştırıp çalıştırırken hiç bir sıkıntı olmuyor. Fakat gel gelelim bu kodları parçalayıp bir sabitler.h ve sabitler.c diye bir dosyanın içerisine atıp main.c dosyasında çağırınca işte orada film kopuyor ve program bir türlü çalışmıyor. Acaba bu konuda yardımcı olabilir misiniz?

    Not: C’de yeniyim hocam affınıza sığınarak soruyorum.d

    1. Estağfirullah Aykut Bey, hepimiz başlangıç aşamasında bu tür hatalar ile karşılaşıyoruz. Hem anlatılanları bir nebze pekiştirmeye çalıştığınız için, hem de yılmayıp çözüm aradığınız için açıkçası tebrik etmek isterim. Sorun yaşadığınız kodları gmail adresime gönderirseniz, memnuniyetle hataları bulup düzelterek yardımcı olurum. İyi çalışmalar dilerim.

  4. Muhammet

    Hocam Merhabalar,

    Eğlenceli üslubunuzda C dilinin bazı önemli noktalarına değinmişsiniz. Yazılarınızı keyifle takip ediyorum. Bu yazınızda void işaretçilerle ilgili verdiğiniz örneği codeblocks derleyicisinde derlemek istedim “error: void value not ignored as it ought to be” şeklinde bir hata verdi. Bunun sebebi ne olabilir. Teşekkürler.

Leave a Reply