C++ Göstericiler (Pointer) C++ Dilinde Gösterici(Poniter) İşlemleri Gösterici nedir? Bir değişkenin bellek adresini tutan değişkene gösterici denir. Yani, gösterici bir tür değişkendir ve başka bir değişkenin adresini barındır. Özellikle donanım seviyesinde çalışmalar yapıyorsanız yani otomasyon ve robot yazılımları vs. geliştiriyorsanız baya faydalı bir şeymiş bu pointerlar bende sizinle beraber öğrendim bunu şuan Göstericiler dizilerde, fonksiyonlarda ve metin işlemlerinde de çokça kullanılırmış. Göstericiler C tabanlı dilleri diğer dillerden ayıran en önemli konudur. Donanım seviyesinde işler yapmanızı sağlar ve aslında bakarsanız bu C ve C++ dilinin en güçlü özelliklerinden biridir. Belirli bir adresteki alabilir, bitleri sağdan sola kaydırabilir, AND, OR veya NOT işlemler yapabilirsiniz. Bu beni gerçekten heyecanlandırdı. Neden mi? Pointerlar oyun hile yapımında kullanılan en önemli şeydir. Sadece oyun değil çeşitli programları kırmak için pointerlar kullanılıyormuş. Yazımın ilerleyen kısımlarında bunlardan bahsedeceğim. Çünkü bu konunun gerçekten sıkıcı bir konu olmadığını hatta eğlenceli ve aşırı meraklandırıcı, gizemli bir konu olduğunu hissediyorum ve bunu size göstermeye çalışacağım. İşlerin gerçekten ilerlediğini bu konuyu dinleyip anladığım zaman anladım. Daha 1. Sınıf öğrencisi olmama rağmen gerçekten ciddi güçlü bir silahı elime almışım gibi düşünüyorum. Daha bildiklerim bilmediklerimin 1 milyonda
biridir hatta daha fazladır ama bu konunun vaat ettiklerini düşününce beni çok heyecanlandırıyor. Neyse devam edersek bu işler özellikle elektronik mühendisliğinde çok önemliymiş neredeyse tüm elektronik mühendislerine C/C++ öğretiliyormuş. Ben bilgisayar mühendisliği okuyorum Fahrettin abim öyle demiş ben onun kitabından alıntı alıntı yapıyorum aralara kendi düşüncelerimi ve bilgilerimi de katarak tabiki. Hadi devam.. Bilgisayar mühendisliğinde ise fonksiyonlar, diziler vb. yapılar çok kullanıldığından ve göstericiler de fonksiyon ve dizilerle ilgili bazı işlemleri kolaylaştırdığından dolayı C++ özellikle tercih edilmektedir. Göstericileri anlayabilmek için RAM belleğin yapısını iyi bilmek gerekir. RAM bellek alt alta dizilmiş raflar şeklinde tasarlanmıştır ve her rafta bir değişken veya veri saklanmaktadır. RAM bellekteki her rafın bir adresi vardır. Her veri belli bir adresteki rafa konulur ve gerektiğinde bu adresten/raftan alınır. Biraz örnek üstünde bakalım geleceğin hacker adayları.. Eğer aşağıdaki komutu (int a=5;) çalıştırırsanız int tipinde bir a değişkeni tanımlarsınız ve içine 5 değerini atarsınız. Arka plana ise aslında RAM bellekteki herhangi bir adrese 4 byte lık bir alan ayrır ve içine ikili sayı olarak 5 değeri yazılır. İlgili değişken yada verinin hangi adreste olacağına ise derleyicimiz karar verir. (Eğer assembly ile kod yazarsanız siz de karar verebilirsiniz.) Mütiş bişeymiş yine heyecanlandım bakın bunuda bilmiyodum bende sizin gibi yeni yeni öğreniyorum bu işleri İnt a=5; Bir değişkenin hangi adreste oluşturulacağına biz karar veremiyoruz ama istersek bu değişkenin adresini bulabiliyoruz. Herhangi bir bir değişkenin adresini öğrenebilmek için adres operatörünü (&) kullanıyoruz. Örneğin a değişkeninin RAM
belleğin hangi adresinde olduğunu öğrenmek için cout<<&a; komutunu kullanabilirsiniz. Aşağıdaki kodları yazıp çalıştırırsanız a değişkeninin RAM bellekteki değerinin ve adresini görebilirsiniz. [crayon-59db0ac4408c1663520992/] Ekran Çıktısı: Gösterici Tanımlama Artık değişkeni ve değişenin değerinin RAM bellekteki adresini öğrendiğimize göre gelelim gösterici değişkenini tanımlamaya. Hatırlarsanız göstericiler başka bir değişkenin adresini barındıran bir değişkendir diye öğrenmiştik. Bir gösterici tanımlamak için değişken önüne yıldız (*) karakteri koyulur ve aşağıdaki kalıp kullanılır: veri tipi *değişken adı; Dikkat ederseniz normal bir değişkenden tek farkı adının önünde bir yıldız karakterinin olmasıdır. Aşağıdaki komutla bir gösterici oluşturalım ve RAM bellekteki duruma bakalım: int *g; Yukarıdaki komut çalınca derleyici g değişkeni için RAM bellekte bir yer yada bir adres ayırır.
Şu ana kadar g adlı göstericinin normal bir değişkenden herhangi bir farkını göremedik. Eğer g göstericisine a değişkeninin adresini atarsanız farkı ve işlevi ortaya çıkacaktır. int *g=&a; Yukarıdaki komut çalışınca derleyici g değişkeni için RAM bellekte bir yer veya adres ayırır. Yukarıdaki komutta gösterici değişkene herhangi bir değer atanmadığı için gösterici değişkeninin içi boş olur. Şu ana kadar g adlı değişkenin normal bir değişkenden farkını göremedik. Eğer g göstericisine a değişkeninin adresini atarsanız farkı ve işlevi ortaya çıkacaktır. int *g=&a; Bu komutun çalışması sonucunda a değişkeninin adresi g göstericisinin içine atanmıştır. Artık a değişkeninin bellek adresi g değişkeninde bulunmaktadır. Aşağıdaki kodları dikkatli inceleyerek yaptığımız işlemlerin sonucunu daha rahat anlayabiliriz. Dikkat ederseniz a değişkeninin bellek adresi ile göstericinin değeri aynıdır. Yani göstericiler belli bir değişkenin RAM bellekteki adresini barındırırlar. [crayon-59db0ac4408d6276674891/] Ekran Çıktısı:
Evet biraz uzun oldu ama sanırım artık bu göstericinin de bir değişken olduğunu ve başka bir değişkenin adresini sakladığını anladınız. Yani, gösterici değişkenler belli bir adresteki verileri gösterirler. Dolayısıyla, değişken adı yerine değişkenin göstericisinde kullanılabilir. Aşağıdaki kodları incelerseniz ne demek istediğimi daha rahat anlayacaksınız. [crayon-59db0ac4408df333278039/] Yukarıdaki kodların çalışması sonucunda *pd göstericisinin d değerini ifade ettiğini anlayabiliriz. Bu özlellik dizilerde ve fonksiyonlarda sık sık kullanılır. Gösterici Aritmatiği Göstericiler kullanılırken, bazen göstericinin gösterdiği adres taban alınıp, o adresten önceki veya sonraki adreslere erişilmesi istenebilir. Bu durum, göstericiler üzerinde, aritmetik işlemcilerin kullanılmasını gerektirir. Göstericiler üzerinde yalnızca toplama (+), çıkarma (-), bir arttırma (++) ve bir eksiltme (--) operatörleri işlemleri yapılabilir. Aşağıdaki gibi üç tane gösterici bildirilmiş olsun: [crayon-59db0ac4408eb399878580/] Bu göstericiler sırasıyla, bir karakter, bir tamsayı ve bir
gerçel sayının bellekte saklanacağı adreslerini tutar. Herhangi bir anda, tuttukları adresler de sırasıyla 10000 (0x2710), 20000 (0x4e20) ve 30000 (0x7530) olsun. Buna göre aşağıdaki atama işelemlerinin sonucu: [crayon-59db0ac4408f2181130467/] sırasyla 10001 (0x2711), 20004 (0x4e24) ve 30008 (0x7538) olur. Bir göstericiye ekleme yapıldığında, o anda tuttuğu adres ile eklenen sayı doğrudan toplanmaz. Böyle olsaydı, bu atamaların sonuçları sırasıyla 10001, 20001 ve 30001 olurdu. Gerçekte, göstericiye bir eklemek, göstericinin gösterdiği yerdeki veriden hemen sonraki verinin adresini hesaplamaktır. Genel olarak, bir göstericiye n sayısını eklemek (veya çıkarmak), bekllekte gösterdiği veriden sonra (veya önce) gelen n. elemanın adresini hesaplamaktır. Buna göre aşağıdaki atamalar şöyle yorumlanır. [crayon-59db0ac4408f9390030877/] Aşağıda verilen kodu incelersek ne demek istediğimi anlayacaksınız. [crayon-59db0ac4408ff994644210/] Gösterici ve Diziler Arasındaki İlişki
C dilinde göstericiler ve diziler arasında yakın bir ilişki vardır. Bir dizinin adı, dizinin ilk elemanının adresini saklayan bir göstericidir. Bu yüzden, bir dizinin herhangi bir elemanına gösterici ile de erişilebilir. Örneğin: [crayon-59db0ac440906099207860/] şeklinde bir bildirim yapılsın. Buna göre aşağıda yapılan atamalar geçerlidir: [crayon-59db0ac440910312791709/] İlk iki satırdaki atamalar aynı anlamdadır. Dizi adı bir gösterici olduğu için, doğrudan aynı tipteki bir göstericiye atanabilir. Ayrıca, i bir tamsayı olmak üzere, [crayon-59db0ac440917117152187/] ile [crayon-59db0ac44091d991204135/] aynı anlamdadır. Bunun sebebi, p göstericisi kutle dizisinin başlangıç adresini tutmuş olmasıdır. p+i işlemi ile i+1. elemanın adresi, ve *(p+i) ile de bu adresteki değer hesaplanır. NOT Bir dizinin, i. elemanına erişmek için *(p+i) işlemi yapılması zorunludur. Yani [crayon-59db0ac440924604608043/] anlamındadır. Çünkü, * operatörü + operatörüne göre işlem önceliğine sahiptir. [crayon-59db0ac44092a315833818/]
Gösterici ile Fonsiyon İlişkisi Göstericiler fonksiyona parametre göndermek için kullanılırlar. Aşağıdaki kodlarda bir fonksiyona parametre olarak bir değişkenin gönderilmesini görebilirsiniz. Burada değişken adlı bir değişkenin bir kopyası oluşturulmakta ve parametre olarak fonsiyona gönderilmektedir. Ayrıca fonksiyonda elde edilen sonucun bir kopyası oluşturulmakta ve ana programa gönderşlmektedir. Doğal olarak buradaki kopyalama işlemleri ek yük getirir ve programın performansı düşebilir. [crayon-59db0ac440931711700383/] Kopyala ile kaybedilen zamanın dışında fonksiyona değer göndermenin daha başka sıkıntıları da var. İsterseniz önce bir hatırlatma yapayım: fonksiyon veya blok içinde tanımlanan bir değişken fonksiyonun veya bloğun çalışması bittikten sonra bellekten silinir. Bu bilgiyide dikkate alıp aşağıdaki kodları incelersek fonksiyona değer göndermenin ne gibi sıkıntılar açtığını daha iyi anlamış olacağız. [crayon-59db0ac440937344948947/] Yukarıda kodlarda art() fonksiyonu bir değişkeninin değerini 10 arttırmak için kullanılır. Yazılan kodlarda a değişkenine önce 10 değeri atanmış ve değişkenin çıktısı alınmıştır. Daha sonra a değişkeni art() fonksiyonuna parametre olarak
gönderilerek değerinin 10 arttırılması amaçlanmıştır. Eğer kodları yazıp alıştırırsanız art() fonksiyonunun a değerini arttırmadığını görürsünüz. Çünkü fonksiyona gönderilen değer önce belleğe kopyalanır ve daha sonrada fonksiyon tarafından kullanılır. Kopyalama işlemlerinin getirdiği ek yükten ve diğer sıkıntılardan kurtulmak için göstericiler kullanılmaktadır. Fonksiyona parametre olarak göstericiler gönderildiğinde herhangi bir kopyalama yapılmamakta, doğrudan adresi iletilmektedir. Fonksiyon göstericinin adresindeki değeri kullanarak işlem yaptığı için daha iyi performans sağlanmaktadır. [crayon-59db0ac440942648794892/] Referanslar(References) Referans değişken, zaten var olan bir değişkene verilen bir tür takma isimdir. Daha önceden oluşturulan bir değişkene bir tama isim/referans verilirse bu değişkeni ister adıyla isterseniz referansıyla kullanabilirsiniz. Basit bir tanım yapmak gerekirse, refereans bir değişkenin RAM bellek adresini gösteren bir etikettir. Bir referans değişken tanımlamak için aşağıdaki yazım kuralı kullanılabilir: veri_tipi& referans=baslangıc degeri; [crayon-59db0ac440949925410756/]