YAPISAL PROGRAMLAMA Hafta-8 AR. GÖR. ESRA SÖĞÜT
İçindekiler Değişken ve Bellek Adresi Pointer Örnekler Kaynaklar
Değişken ve Bellek Adresi Örneğin tam adlı bir tamsayı değişkenini aşağıdaki gibi tanımladığımızı varsayalım: int tam = 33; Bu değişken için, int tipinde bellekte (genellikle herbiri 1 bayt olan 4 bayt büyüklüğünde) bir hücre ayrılır ve o hücreye 33 sayısı ikilik (binary) sayı sitemindeki karşılığı olan 4 baytlık (32 bitlik): 00000000 00000000 00000000 00100001
Değişken ve Bellek Adresi 00000000 00000000 00000000 00100001 sayısı elektronik olarak yazılır. tam değişkenine ait dört temel özellik Şekil'deki gibi gösterilebilir:
Değişken ve Bellek Adresi Bellek adresleri genellikle onaltılık (hexadecimal) sayı sisteminde ifade edilir. 0x3fffd14 sayısı onluk (decimal) sayı sisteminde 67108116 sayına karşık gelir. Bunun anlamı, tam değişkeni, program çalıştığı sürece, bellekte 67108116. - 67108120. numaralı gözler arasındaki 4 baytlık hücreyi işgal edecek olmasıdır. Gerçekte, int tipindeki tam değişkeninin bellekteki yerleşimi ve içeriği (değeri) Şekil'de gösterildiği gibi olacaktır.
Değişken ve Bellek Adresi tam adlı değişkenin bellekteki gerçek konumu ve ikilik düzendeki içeriği
Değişken ve Bellek Adresi Değişkenin saklı olduğu adres, & karakteri ile tanımlı adres operatörü ile öğrenilebilir. Bu operatör bir değişkenin önüne konursa, o değişkenin içeriği ile değil adresi ile ilgileniliyor anlamına gelir. Aşağıdaki program parçasının: int tam = 33; printf("icerik: %d\n",tam); printf("adres : %p\n",&tam); çıktısı: icerik: 33 adres : 3fffd14
Değişken ve Bellek Adresi şeklindedir. Burada birinci satır tam değişkeninin içeriği, ikinci ise adresidir. Adres yazdırılırken %p tip belirleyicisinin kullanıldığına dikkat ediniz
Pointer (Gösterici) Göstericiler, değer olarak hafıza adreslerini içeren değişkenlerdir. Normalde bir değişken doğrudan kesin bir değeri içerir. Göstericiler ise kesin bir değeri tutan değişkenlerin adresini içerir. Bu bağlamda, bir değişken ismi bir değeri doğrudan belirlerken, göstericiler değeri dolaylı yoldan belirtir. Bir değeri gösterici ile belirtmek dolaylama yapmak ( indirection ) olarak adlandırılır. Göstericiler de diğer değişkenler gibi kullanılmadan önce bildirilmelidir. pointer = işaretçi = gösterici = gösterge
Pointer (Gösterici) int *sayiciPtr, sayici; biçiminde bir bildirim, sayiciPtr değişkenini int * (bir tamsayıyı gösteren gösterici) tipinde bildirir. Bu bildirim “sayiciPtr bir int göstericisidir” ya da “sayiciPtr , tamsayı tipinde bir nesneyi gösterir” biçiminde okunur. Ayrıca, sayici değişkeni de tamsayı olarak bildirilmiştir. Bildirim içindeki * yalnızca sayiciPtr’ye uygulanır. Bildirimlerde * bu biçimde kullanıldığında, bildirilen değişkenin gösterici olduğunu belirtir. Göstericiler herhangi bir veri tipindeki nesneleri göstermek için bildirilebilirler.
Pointer (Gösterici) int *sayiciPtr, sayici; Bir değişkeni doğrudan ve dolaylı olarak belirlemek
Pointer Operatörleri & ya da adres operatörü, operandının adresini döndüren bir tekli operatördür. Örneğin , int y = 5; int *yPtr; bildirimlerini ele aldığımızda yPtr =&y; ifadesi, y değişkeninin adresini yPtr gösterici değişkenine atar. Buna, yPtr değişkeni y’yi göstermektedir denir. Şekil, az önceki atama çalıştırıldıktan sonra hafızanın şematik tasvirini göstermektedir.
Pointer Operatörleri * operatörü ya da genellikle söylendiği biçimde içerik operatörü, operandının(yani göstericinin) gösterdiği nesnenin değerini döndürür. Örneğin, printf ( “%d”,*yPtr ); ifadesi, y değişkeninin değerini yani 5’i yazdırır. Bu şekilde bir * kullanımına, göstericinin gösterdiği nesneye erişmek denir.
Pointer Operatörleri Bir göstericiye, bir değişkenin adresini atamak için adres operatörünü kullanabiliriz. Örneğin tamsayı tipindeki tam adlı bir değişken ve ptam bir gösterici olsun. Derleyicide, aşağıdaki gibi bir atama yapıldığında: int *ptam, tam = 33; . . . ptam = &tam; ptam göstericisinin tam değişkeninin saklandığı adresi tutacaktır. Bu durum Şekil’deki gibi tasvir edilir.
Pointer Operatörleri Şekil'deki gösterimde, ptam göstericisinin içeriği tam değişkeninin içeriği (33) değil adresidir (0x3fffd14). Ayrıca, ptam değişkeni, bellekte başka bir hücrede saklandığına ve bu hücrenin int değil int * tipinde bir bölge olduğuna dikkat ediniz.
Pointer-Örnek #include <stdio.h> int main() { int *ptam, tam = 33; //değişkenler bildirilmiş ptam = &tam; // atama ile tam değişkeninin adresi, ptam göstericisine atanmıştır. Bu satırdan itibaren ptam, tam değişkeninin gösterir printf("tam: icerik = %d\n", tam); //tam'ın içeriği (33 sayısı) printf("tam: adres = %p\n",&tam); // tam'ın adresi, %p tip karakteri ile, ekrana yazdırılmıştır printf("tam: adres = %p\n",ptam); // tam'ın adresi, %p tip karakteri ile, ekrana yazdırılmıştır return 0; }
Pointer-Örnek Ekran çıktısı incelendiğinde, &tam ve ptam içereriğinin aynı anlamda olduğu görülür. ÇIKTI: tam: icerik = 33 tam: adres = 0x3fffd14
Pointer-Örnek tam adlı değişkenin içeriğine ptam gösterici üzerinde de erişilebilir. Bunun için program içinde ptam değişkeninin önüne yönlendirme operatörü (*) koymak yeterlidir. Yani*ptam, tam değişkeninin adresini değil içeriğini tutar. Buna göre: *ptam = 44; komutuyla, ptam'ın adresini tuttuğu hücreye 44 değeri atanır. Bu durum, aşağıdaki örnekte gösterilmiştir.
Pointer-Örnek #include <stdio.h> int main() { int *ptam, tam = 33; ptam = &tam; /* ptam -> tam */ printf("&tam = %p\n",&tam); printf("ptam = %p\n",ptam); printf("\n"); printf("tam = %d\n",tam); printf("*ptam = %d\n",*ptam); *ptam = 44; /* tam = 44 anlamında */ return 0; } ÇIKTI: &tam = 0x3fffd14 ptam = 0x3fffd14 tam = 33 *ptam = 33 tam = 44 *ptam = 44
Özet Özetle ptam=&tam atamasıyla: *ptam ve tam, tam adlı değişkenin içeriği ile ilgilidir. ptam ve &tam, tam adlı değişkenin adresi ile ilgilidir. * yönlendirme ve & adres operatörüdür.
Pointer-Örnek /*& ve * operatörlerini kullanmak */ #include <stdio.h> int main( ) { int a; // a bir tamsayıdır int *aPtr; // aPtr bir tamsayıyı gösteren bir göstericidir a = 7; aPtr = &a; // aPtr ‘ye a’nın adresi atandı printf( "a’nın adresi %p" "\naPtr değişkeninin değeri %p", &a, aPtr ); printf( "\n\na’nın değeri %d" "\n*aPtr nin değeri %d", a, *aPtr ); printf( "\n\n* ve & birbirlerinin" "eşleniğidir.\n&*aPtr = %p" "\n*&aPtr = %p\n", &*aPtr, *&aPtr ); return 0; }
Pointer Tutan Pointer’lar
Referansla Argüman Aktarımı Kendisine verilen iki sayının yerlerini değiştiren bir fonksiyon yazalım. Yani kendisine a ve b adında iki değişken yollanıyorsa, a'nın değerini b; b'nin değerini ise a yapsın. Referans yoluyla aktarım olmasaydı, iki değişkenin değerlerini fonksiyon kullanarak değiştiremezdik. Eğer yazdığınız fonksiyon birden çok değer döndürmek zorundaysa, referans yoluyla aktarım zorunlu hâle geliyor. Çünkü daha önce işlediğimiz return ifadesiyle sadece tek bir değer döndürebiliriz.
Örneğin bir bölme işlemi yapıp, bölüm sonucunu ve kalanı söyleyen bir fonksiyon yazacağımızı düşünelim. Bu durumda, bölünen ve bölen fonksiyona gidecek argümanlar olurken; kalan ve bölüm geriye dönmelidir. return ifadesi geriye tek bir değer vereceğinden, ikinci değeri alabilmek için referans yöntemi kullanmamız gerekir.
Bu programa göre, a, b ve c'nin değerleri nedir?
Kaynaklar Atılım Üniversitesi-Yrd. Doç. Dr. Çiğdem Turhan - Yrd. Doç. Dr. Fatma Cemile Serçe-C++ Dersi Sunumları Anadolu Üniversitesi- Öğr. Gör. Levent Terlemez-Bilgisayar Programlama Ders Notları C ve C++ Deitel&Deitel Programlama Kitabı İTÜ-Dr. Feza Buzluca-Nesneye Dayalı Programlama Dersi Notları Gazi Üniversitesi-Doç. Dr. Ercan Nurcan YILMAZ C++ Dersi Sunumları C Programlama Dersi – X - Çağatay ÇEBİ