gösterici gösteren gösterici (pointer to pointer)

Gösterici değişkenler (pointer variables) değerleri adres olan yani adres bilgisi tutan değişkenlerdir.

gibi bir tanımlamayla ismi ptr olan bir değişken oluşturulmuş olur. Programın çalışma zamanında bu nesne için bellekte ayrılan yer sistemden sisteme değişebilecek şekilde 2, 4 ya da 8 byte olabilir.
ptr değişkeni int * türündendir. ptr bir değişken olduğuna göre, ptr değişkeninin de adresinden söz edilebilir, değil mi?
Adres işleci ile ptr değişkeninin kendi adresini elde edebiliriz:

Yukarıdaki ifadenin türü nedir? Bu ifadenin türü, int * türünden bir nesnenin adresi olan türdür. Bu tür C dilinde

olarak ifade edilir. O zaman yukarıdaki ptr gibi bir değişkenin adresi, bir başka değişkende tutulmak istenirse, bu adresi tutacak değişkenin türü int ** olmalıdır. Aşağıdaki koda bakalım:

main işlevinde int ** türünden olan pp isimli değişkene, int * türünden olan ptr değişkeninin adresi ile ilk değer veriliyor. Bunun anlamı şudur: ptr değişkeninin değeri, p değişkeninin adresidir. Başka bir deyişle, pp isimli gösterici değişken bir başka gösterici değişken olan ptr‘yi göstermektedir. Bu durumda

ifadesi pp nesnesinin gösterdiği nesne, ptr nesnesinin kendisidir. *pp ifadesine yapılan atama aslında ptr nesnesini değiştirir. Aşağıdaki programı inceleyin:

main işlevinde tanımlanan int * türden ptr isimli gösterici değişkene int türden x değişkeninin adresi ile, int ** türden pp isimli gösterici değişkene ise ptr‘nin adresi ile ilk değer veriliyor.

deyimiyle, ptr gösterici değişkeninin gösterdiği nesneye, yani x değişkenine 100 değeri atanmış oluyor.

deyimi ile pp gösterici değişkeninin gösterdiği nesneye, yani p gösterici değişkenine bu kez y değişkeninin adresi atanıyor. Bu atamadan sonra yürütülecek

ataması ile artık ptr gösterici değişkeninin gösterdiği nesneye yani y değişkenine 200 değeri atanmış olur. Şimdi de aşağıdaki deyimi inceleyeleim:

İçerik işlecinin işleç öncelik tablomuzun ikinci seviyesinde yer aldığını ve sağdan sola öncelik yönüne sahip olduğunu biliyorsunuz. Bu durumda önce

ifadesi ile pp gösterici değişkeninin gösterdiği nesneye yani ptr nesnesine erişilir. Daha sonra

ifadesiyle de pp gösterici değişkeninin gösterdiği nesnenin gösterdiği nesneye, yani p değişkeninin gösterdiği nesneye, yani y değişkenine erişilir. Bu deyimin yürütülmesiyle y değişkenine 2000 değeri atanmış olur.
**pp ifadesi, pp‘nin gösterdiği nesnenin gösterdiği nesneye, yani y nesnesine karşılık gelir.

Şimdi de aşağıdaki kodu inceleyelim:

Yukarıdaki kodda p gösterici değişkeni a dizisini, pp gösterici değişkeni ise p gösterici değişkenini gösteriyor.

deyimi ile pp‘nin gösterdiği nesneyi yani p’yi 1 arttırmış oluyoruz. p‘nin 1 artması demek artık a dizisinin 1 indisli öğesini gösteriyor olması demek, değil mi?

deyimi ile ise pp‘nin gösterdiği nesnesinin gösterdiği nesneyi yani a dizisinin 1 indisli öğesini 1 arttırmış oluyoruz.

deyiminde ise, *pp gösterici değişkeninin yani p’nin gösterdiği dizinin 2 indisli öğesine -1 değeri atanıyor. *pp, yani p,  a dizisinin 1 indisli öğesini gösterdiğine göre bu ifade ile a dizisinin 3 indisli öğesine -1 değeri atanmış olur.

yerel bir gösterici değişkenin değerini değiştiren işlevler

Yerel bir nesnenin değerini değiştirecek bir işlev, yerel nesnenin adresi ile çağrılmalıdır (call by reference). Yerel bir gösterici değişkenin değerini değiştirecek bir işlev de, yerel göstericinin değerini değil adresini almalıdır:

Örnek kodda int * türünden iki nesnenin değerini takas etmek amacıyla swap_ptr isimli bir işlev tanımlanıyor. İşlevin int ** türünden iki parametresi olduğunu görüyorsunuz. Bu işlev şüphesiz değerlerini takas edeceği nesnelerin adresleri ile çağrılmalıdır:

main işlevi içinde int türden x ve y isimli değişkenler ile p ve q isimli gösterici değişkenler tanımlanıyor. Verilen ilk değerlerle p, x‘i, q ise y‘yi gösteriyor.
Daha sonra, çağrılan swap_ptr işlevine p ve q gösterici değişkenlerinin adresleri gönderiliyor. İşleve yapılan çağrıdan sonra artık, p gösterici değişkeni y nesnesini, q gösterici değişkeni ise x nesnesini gösterir hale geliyor.
Şimdi de aşağıdaki işlevi inceleyin:

pp_swap isimli işlev hangi nesneleri takas ediyor? İşlevimiz int * türünden iki göstericinin adresini alarak bunların gösterdiği int türden nesneleri takas ediyor:

bir gösterici dizisi üzerinde işlem yapan işlevler

Bir dizi üzerinde işlem yapan işlevin dizinin başlangıç adresi ile dizinin boyutunu alması gerektiğini biliyorsunuz. int türden bir dizi ile ilgili işlem yapan bir işlevin bildirimi şöyle olabilir:

Böyle bir işlev için eşdeğer bir başka bildirim şöyle olabilir:

Böyle bir işlev dizinin ilk elemanının adresi ve dizinin boyutu ile çağrılır, değil mi?

gibi bir dizi söz konusu olduğunda, dizi ismi olan a bir ifade içinde kullanıldığında otomatik olarak bu dizinin adresine dönüştürülür (array to pointer conversion). Yani derleyici açısından bakıldığında a ifadesi

ifadesine eşdeğerdir. Bu işlev

biçiminde çağrılabilir.

Bu kez elemanları int * türden olan bir dizi tanımlanmış olsun:

Yine a ifadesi bir işleme sokulduğunda, bu dizinin ilk elemanı olan nesnenin adresine yani dizinin başlangıç adresine dönüştürülür.

Bu dizinin ilk elemanı olan nesne int * türünden olduğuna göre, bu nesnenin adresi int ** türündendir. Böyle bir dizi üzerinde işlem yapacak işlev, bu dizinin başlangıç adresi ile boyutunu alacağına göre, şöyle bildirilmelidir:

Böyle bir işlev için alternatif bir bildirim şöyle olabilir:

Derleyicinin yaptığı kontroller açısından her iki bildirim eş değerdedir. Bazı kodlayıcılar, işlevin bir dizi adresi istediği vurgusunu yaptığı için yukarıdaki bidirimi tercih ederler.
Bu işlev

biçiminde çağrılabilir.

gösterici gösteren gösterici ve const anahtar sözcüğü

Gösterici gösteren göstericilerde const anahtar sözcüğünün kullanıldığı yere bağlı olarak const anahtar sözcüğünün anlamı ve buna bağlı olarak derleyicinin yaptığı kontroller değişir.
Şimdi tüm senaryoları tek tek inceleyelim. Önce aşağıdaki koda bakalım:

Yukarıdaki kodda pp göstericisinin

biçiminde tanımlandığını görüyorsunuz. Burada const olan pp değişkeninin kendisidir. pp değişkeni hayatı boyunca, kendisine ilk değer olarak verilen p1 değişkeninin adresini tutacaktır. pp değişkeninin değerini değiştirmeye yönelik ifadeler geçersizdir. Ancak *pp nesnesine, yani pp‘nin gösterdiği nesneye ya da **pp‘ye, yani pp‘nin gösterdiği nesnenin gösterdiği nesneye yapılacak atamalar geçerlidir. Burada tanımlanan pp‘yi sözel olarak şu şekilde ifade edebiliriz: Bir göstericiyi gösteren kendisi const gösterici (const pointer to int *).

Şimdi de aşağıdaki koda bakalım:

Burada ise pp isimli göstericinin

biçiminde tanımlandığını görüyorsunuz. Bu durumda const olan *pp nesnesidir. *pp nesnesine yapılan atamalar geçersizdir. Yani pp‘nin gösterdiği nesneyi pp yoluyla değiştiremeyiz.  pp ve **pp nesnelerine yapılan atamalar geçerlidir. Burada tanımlanan pp‘yi sözel olarak şu şekilde ifade edebiliriz: Kendisi const bir göstericiyi gösteren gösterici (pointer to const pointer to int / pointer to top level const pointer).

Son olarak aşağıdaki koda bakalım:

Burada pp isimli göstericinin

biçiminde tanımlandığını görüyorsunuz. Tanımlama aşağıdaki gibi yapılsaydı da bir anlam değişikliği olmayacaktı:

Bu durumda const olan **pp nesnesidir. **pp nesnesine yapılan atamalar geçersizdir. Yani pp‘nin gösterdiği göstericinin gösterdiği nesneyi pp yoluyla değiştiremeyiz. pp ve *pp nesnelerine yapılan atamalar geçerlidir. Burada tanımlanan pp‘yi sözel olarak şu şekilde ifade edebiliriz: Gösterdiği yer const olan bir göstericiyi gösteren gösterici. (pointer to pointer to const int / pointer to low level const pointer).

Aklınızda kalması için şu basit kuralı anımsayın: const anahtar sözcüğü neden önce geliyorsa const olan odur:

const anahtar sözcüğü pp‘den önce gelmiş. const olan pp. pp‘ye değer atayamayız.

const anahtar sözcüğü *pp‘den önce gelmiş. const olan *pp. *pp‘ye değer atayamayız.

const anahtar sözcüğü **pp‘den önce gelmiş. const olan **pp. **pp‘ye değer atayamayız.

const anahtar sözcüğü bu yerlerin hepsinde birden kullanılabilir:

pp kendisi const ve gösterdiği yer const olan bir göstericiyi gösteren kendisi const bir göstericidir. (pp is a const pointer to a const pointer to const int)

Şimdi de aşağıdaki programı inceleyin:

Yukarıdaki programda tanımlanan işlevlere inceleyelim:
swap_ptr işlevi kendisine gönderilen const char * türünden iki gösterici değişkeni takas ediyor.
display_str_array işlevi adresini ve boyutunu aldığı öğeleri const char * türünden olan bir gösterici dizisinin öğelerinin gösterdiği yazıları standart çıkış akımına yazdırıyor.
sort_str_array işlevi adresini ve boyutunu aldığı öğeleri const char * türünden olan bir dizinin öğelerini, gösterdiği yazılar küçükten büyüğe doğru olacak şekilde sıralıyor.

Necati Ergin

C ve Sistem Programcıları Derneğinde eğitmen olarak çalışıyor.

Bunlar da ilginizi çekebilir

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Kod Eklemek İçin Okuyun