Merhaba, tekrar hoş geldiniz.
Bu derste, pass by reference adı verilen bir kavramdan bahsetmek istiyorum.
İşaretçiler hakkında ilk konuşmaya başladığımızda, avantajlarından birinin global değişkenleri çok fazla
kullanmak zorunda olmamanız olduğundan bahsetmiştim.
Çünkü bir işleve bir işaretçi aktardığınızda,
işlevler bu parametreyi kullanabilir. Ve bu parametreyi değiştirdiğinde, veriler fonksiyonun
dışında değiştirilecektir.
Yani bu çok ama çok değerli bir kavram.
Bunun hakkında konuşacağız. Bu daha çok bir konsept. C aslında bunu kullanmıyor.
Size bunun nasıl olduğunu göstereceğim ve temelde nasıl çalıştığına
dair bazı örnekler vereceğim.
Yani bu değer üzerinden geçiş kavramı var. Bunun işlevlerle ilgisi var.
Bir fonksiyona veri aktarmaktan bahsettik, ancak derleyicideki dil açısından
gerçekte nasıl aktarılır.
Yani değere göre geçiş diye bir kavram var. Şu ana kadar konuştuğumuz
şey de buydu. Scanf fonksiyonu dışında
fonksiyonu her çağırdığımızda, fonksiyonun içindeki
veriyi değiştirdiğinizde, fonksiyonun dışında
değiştirilmez.
Farklı bir kavram olan yerel değişkenlerden bahsetmiştik.
Ancak bir fonksiyona veri aktardığınızda, bunu aktarmanın iki yolu vardır.
Değer olarak aktarabilirsiniz, referans olarak aktarabilirsiniz.
C sadece değerle geçiş yapar ve java da
öyle, hepsi değerle
geçiş yapar. Ancak adresleri kopyaladığınız için referansla
geçişi simüle eder.
Bu nedenle, bu referansla geçiş
kavramı c tarafından özel olarak kullanılmasa
da, onu taklit eder. Referans yoluyla geçişin ne anlama geldiğini ve bunun
neden önemli olduğunu anlamak için bu konu hakkında konuşacağız.
Ancak pass by value, bir fonksiyonun bir argümanın gerçek değerini fonksiyonun
resmi parametresine kopyalamasıdır.
İşte bu nedenle, bir tamsayı, float veya double değeriniz
varsa ve bunu bir fonksiyona argüman olarak iletirseniz, bu gerçek değişken
kopyalandığı için fonksiyon dışında değiştirilmez.
Dolayısıyla, işlev içindeki parametrede yapılan değişikliklerin argüman üzerinde hiçbir etkisi yoktur.
Aktarılan argüman değiştirilmez.
Yani bir kopya olduğu için neredeyse yerel bir değişken gibi davranır.
Kaputun altında gerçekte nasıl oluyor, kopyalanıyor ve işleve aktarılıyor.
C programı, argümanları iletmek için değerle çağırmayı kullanır.
Her şey değer ile çağrılır değer ile geçer.
Bu terimler birbirlerinin yerine kullanılmaktadır.
Yani yine bu, bir fonksiyon içindeki kodun fonksiyonu çağırmak için kullanılan argümanları değiştiremeyeceği anlamına gelir.
Fonksiyon içinde değiştirilmiş olmalarına rağmen değerlerde herhangi bir değişiklik yoktur.
Yerel değişkenler gibi görünüyorlar.
İşte bu nedenle, bu noktaya kadar, verileri
bir işlevden diğerine paylaşmak istediğinizde global veri oluşturmanız gerekiyordu.
İşte C böyle çalışır.
Ancak bir adresin kopyasını gerçekten ilettiğinizde
ne olur? Daha sonra referansla geçişi taklit edebilir.
Ancak burada pass by value'yu nasıl kullandığımıza dair bir örnek var, burada çok güzel gösterilmiş.
Bu takas rutini, pass by value'yu göstermek için sıklıkla kullanılan bir
durumdur, çünkü yaptığı şey bir geçici oluşturmak ve argümanların geçirildiği
parametreleri takas etmektir.
Yani iki tamsayı alan swap adında bir fonksiyonumuz varsa, temp
adında bir yerel değişken yaratırız.
Eğer x parametresini temp'e atarsak, temp x'e
eşit olacaktır. Eğer x'in y'ye eşit olduğunu söylersek,
y artık x'e eşit olacaktır. Eğer y sıcaklığa eşittir dersek, y
sıcaklığa eşit olacaktır.
Ancak bu fonksiyon çağrıldığında bu değerler aktarıldığında,
x ve y bu fonksiyon için yerel olacaktır.
Bu değişkenler bu fonksiyon dışında değiştirilmeyecektir.
Ve bu fonksiyon çok kullanışlı olduğunu gösteriyor çünkü
y'ye atama yapıyoruz ve x'e atama yapıyoruz.
Ve x ve y bu fonksiyon için yereldir çünkü değerle geçiyoruz, bu fonksiyonun
dışında değiştirilmiyorlar ve bu yüzden global değişkenler kullanmamız gerekiyor.
Ama onlar tam sayı. Bunlar x ve y olmak üzere iki tam sayıdır. Bunlar adres değil,
tam sayıdır ve bu önemlidir.
Dolayısıyla, swap'ı çağırırsak
ve bunun neye benzediğini görürsek, değerlerin
değişmediğini fark edeceksiniz.
Yani a 100 b ile 200 adında bir değişken yaratırsak
ve değişkenleri takas etmeden önce yazdırırsak, 100
ve 200, sonra takas çağrısı yaparsak,
değişkenler tekrar yazdırıldıktan sonra değişmeyecektir.
Şimdi buna kod blokları halinde bakalım ve bu da pass by value'yu gösterecektir.
İşte örneğimiz. Takas rutinimiz var.
x ve y olarak geçen verilerin değerlerini değiştiriyoruz.
100-200 ile arıyoruz.
Fonksiyonu çağırmadan önce çıktı alacağız.
Sonra çıktısını alacağız.
Ve fark edeceğiniz şey, a ve b değişkenlerinin asla değişmediğidir.
Bunun nedeni, değer bazında geçiş yapıyor olmamızdır.
Bu programı çalıştırırsam, takastan
önce a'nın değerinin 100 olduğunu, takastan sonra a'nın değerinin 100 olduğunu ve aynı şekilde
b'nin 200 ve 200 olduğunu görebiliriz.
Bunun nedeni de değer ile geçiş yapmamızdır. Referansla geçseydik, a ve
b değişkenleri swap çağrısından sonra değişecekti.
Şimdi aynı tekniği pass by value olarak kullanırsak ama
işaretçileri kopyalarsak ne olur sizce?
Yani işaretçiler ve fonksiyonlar birlikte oldukça iyi geçinirler.
Bir işaretçiyi bir işleve argüman olarak aktarabilir
ve bir işlevin sonuç olarak bir işaretçi döndürmesini sağlayabilirsiniz.
Bunun nasıl yapılacağını da göstereceğiz.
Ancak pass by reference, bir argümanın adresini resmi parametreye kopyalar.
Adres, çağrıda kullanılan gerçek argümana erişmek için kullanılır.
Bunun anlamı, fonksiyon içindeki parametrede yapılan
değişikliklerin çağrıdaki argümanı etkilemesidir.
Bu nedenle, teknik olarak referansla geçilen başlık altında olmasa
da, referansla geçişi taklit eder veya simüle eder.
Adresi kopyalıyoruz. Adresi kopyalarsanız, yeni
bir adresiniz ve bu adres değeri için belleğiniz olur.
Ancak bu adrese atama yapmaya çalışırsanız, adrese atama yapacaktır.
Yani değer olarak aktarılsa da kopyalanıyor.
Ve eğer normal bir tamsayıyı kopyalıyorsanız, değişiklik yapmazsınız. Bir adresi kopyalıyorsanız, adres bellektedir,
bu nedenle değişiklik yaptığınızda o adreste değişiklik yapmış olursunuz.
Değeri referans olarak iletmek için,
argüman işaretçileri diğer değerler gibi fonksiyonlara iletilir.
Fonksiyon parametrelerini yıldız işaretiyle birlikte işaretçi tipleri olarak bildirmeniz gerekir.
Fonksiyon içindeki değişiklikler fonksiyon dışına da yansıtılır.
Global değişkenleri çok fazla kullanmanız gerekmez.
Değişikliklerin işlevin dışına yansımadığı değer çağrısından farklı olarak.
Hala kopyalıyoruz, hala değerle geçiyoruz, bu sadece adresin bir kopyasının değeri.
Dolayısıyla bu, anlaşılması gereken önemli bir kavramdır.
Şimdi takas fonksiyonumuzu oluştururken,
fonksiyonun içindeki bildirimi asteriks, yıldız ve
yıldız olarak oluşturacağız.
Bu, bu işlevin işaretçi aldığını söylüyor.
Şimdi bu fonksiyonun içine atama yaptığımızda, değerleri atamak
istediğimiz için aslında dereferans yapmak için dolaylama operatörünü
kullanmamız gerekir. Bu x'in temp'e işaret ettiği
değeri atayacağız.
x'in işaret ettiği değeri y'nin işaret ettiği değere
atayacağız ve ardından y'nin işaret ettiği değeri temp'ye atayacağız.
Temp bir tamsayıdır, bu yüzden onu dereference etmek zorunda değiliz.
Şimdi a ve b'yi geçerek
a100 ve b200 ile çağırdığımızda,
a ve b işaretçi değildir, bu nedenle adresi geçmemiz gerekir çünkü
fonksiyon işaretçi alır.
İşaretçi alan bir işleviniz olduğunda ve bir işaretçi bildirmediğinizde, işlecin
adresini kullanmanız gerekir ve bu scanf'i nasıl kullandığımızla aynı şeydir.
Ancak şimdi bunu swap çağrısından sonra
çağırdığımızda a ve b değişkenlerinin değişeceğini, yer değiştireceğini
fark edeceksiniz. A 200 olacak B 100 olacak.
Şimdi bu örneğe, kod bloklarındaki işaretçilere yönelik takas yöntemi değişikliğimizle bakalım.
Kod bloklarındayız. Takas işlevini işaretçi alacak şekilde değiştirdik.
Bu yüzden onları atarken referanslarını kaldırmamız gerekir.
Artık a ve b'nin adreslerini de aktarıyoruz çünkü bunlar artık tamsayı
ve takas artık işaretçi alıyor. Bunu çalıştırdığımızda, çağrı
ve sonrasının artık değişeceğini göreceğiz.
Yani swap demeden önce a 100'e eşittir swap dedikten sonra ise 200'dür.
B için takası aramadan önce, 200. Sonrası 100.
Böylece, adresin bir kopyasını oluşturduğumuz için fonksiyon içindeki
değişkenlerin değerlerini değiştirebiliyoruz. Çok, çok önemli ve işaretçilerin en
büyük avantajlarından biri.
Sözdizimini özetleyelim. Zaten anlattım ama şimdi daha detaylı inceleyelim.
Bir değişken hakkında iki tür bilgiyi bir fonksiyona iletebilirsiniz.
x'in değerini aktarabilirsiniz
ve fonksiyon x ile aynı türde bildirilmelidir.
Bunu biliyoruz. Bir fonksiyona veri aktardığımızda,
fonksiyonu çağırdığımızda aynı tiplere sahip olmamız gerekir.
Ve burada onu çağırmak için int fonksiyon1'imiz var ve bu bir int num olarak tanımlanmıştır.
Dolayısıyla, x değerini fonksiyona aktardığımızda aynı tipte bildirilmeleri gerekir.
Ve sonra x'in bir adresini gerçekten çağırmak istiyorsanız,
işlev tanımının doğru türe bir işaretçi içermesi gerekir.
Dolayısıyla, işaretçi kullandığınızda, veri aktarıyorsanız ve bu veri bir
işaretçi değilse, verinin adresini almanız gerektiğinden emin olmalısınız.
Bunun özeti, işaretçi almayan bir fonksiyon tanımlarsanız, sadece onu
çağırabilir ve verileri aktarabilirsiniz.
İşaretçi olmadığından emin olun.
Eğer yıldız işaretçisi alan bir fonksiyonunuz varsa,
onu çağırdığınızda add
veya sub operatörü ile çağırmanız gerekir.
Adresi girmeniz gerekiyor.
Sözdizimi bu.
Artık sabit anahtar sözcüğünü kullanarak bir fonksiyon parametresini de niteleyebilirsiniz.
Bu, fonksiyonun bu parametre için aktarılan argümanı bir sabit olarak ele alacağını gösterir.
Yalnızca parametre bir işaretçi olduğunda kullanışlıdır.
Çünkü eğer değer ile geçiyorsanız ve işaretçileri geçirmiyorsanız, bunun bir önemi yoktur
çünkü fonksiyonun dışındaki değişikliği etkilemeyecektir, bu
yüzden neden sabit yapasınız ki, değiştirseniz de kimin umurunda.
Bir işaretçiyi sabit hale getirmenizin tek nedeni, fonksiyon dışındaki değerlerin
değişmesini istememenizdir.
Ve böylece, fonksiyonun argümanın işaret ettiği değeri değiştirmeyeceğini belirtmek
için bir işaretçi olan bir parametreye sabit anahtar sözcüğünü uygulayabilirsiniz.
Çok önemli.
Yani bu örnekte bir mesaj gönderme fonksiyonumuz var. Bir p mesaj işaretçisi
alır.
Sabit anahtar sözcüğünü char yıldızından önce koyuyoruz.
Yani bu tür bir parametre p mesajı sabit bir karaktere işaretçidir.
Sabit olan karakter değeridir, adresi değil.
Yani bu durumda yaptığımız şey, işaret edilen işaretçinin değişemeyeceğidir.
Adresi değiştirebiliriz, ancak değerin işaret ettiği şeyi değiştiremeyiz.
Sözdizimi, diğer işaretçiler üzerinde sabitler oluşturduğumuza benzer.
Sabit anahtar kelime ilk gösterilen şeydir, o zaman değeri değiştiremezsiniz.
Şimdi işaretçinin kendisinin sabit olduğunu belirtebilirsiniz,
ancak adres değer olarak aktarıldığı için bu pek mantıklı değildir.
Çağıran işlevde render işaretçisini
değiştiremezsiniz. Yani bu sadece adresin değiştirilemeyeceği şekilde yapmaya çalışırsanız,
ne anlamı var çünkü değerle aktarılıyor. Yani aslında bu belirli adresi
tutmak için yeni bir bellek konumu
oluşturuyorsunuz. Yani onu değiştirirseniz, dışarıdan değişmeyecek olması kimin umurunda.
Sadece parametreyi sabit yapabilmek istersiniz,
böylece işaret ettiği şeyin değerini değiştirmezsiniz,
orijinal işaretçiyi
ve çağıran işlevi zaten değiştiremezsiniz çünkü değer kopyayla
aktarılır.
Derleyici, sabit veriye işaretçi olan bir argümanın güvenli olacağını
bilir, bu nedenle bunun avantajları vardır. Değerin işaret ettiği şeyi yanlışlıkla değiştiremezsiniz.
Dolayısıyla, değişikliklerin fonksiyonun dışına yansıtılmasını istemiyorsanız bunu tekrar yapabilirsiniz.
Bir parametre için argüman olarak sabit veriye bir işaretçi geçirirseniz,
parametre yukarıdaki gibi kullanılmalıdır.
Fonksiyonu çağırdığınızda ve onu sabit olarak geçirdiğinizde, fonksiyon
bildiriminin de sabit olan diğer normal işaretçiler gibi sabit bir karakter
işaretçisi almasını sağlamalısınız.
Tanımlanmaları ve tanımların prototiple eşleşmesi gerekir.
Bir fonksiyondan işaretçi döndürme.
Bir işlevden bir işaretçi döndürmek özellikle güçlü bir yetenektir.
Yalnızca tek bir değer değil, tüm değerler kümesini döndürmeniz için bir yol sağlar.
İşaretçiler hakkında bahsettiğim diğer avantajlardan
biri de artık bir fonksiyondan daha fazla veri döndürebilmenizdir.
Geçmişte, yalnızca bir değer döndürebiliyordunuz.
Ancak bir işaretçi ile, çünkü bir işaretçi henüz bahsetmediğimiz
bir yapıya veya hatta birden fazla değer döndürebileceğiniz başka şeylere işaret
ediyor olabilir Bu yüzden işaretçileri fonksiyonlara
nasıl geçireceğimizi biliyoruz, şimdi işaretçileri döndürmekten bahsetmek
istiyoruz.
Bir işaretçi döndüren bir işlev bildirmeniz gerekir. Bu yüzden işlev bildiriminin eşleşmesi gerekir.
Bunu yapmak çok kolay. Sadece fonksiyonu bildirirken veya tanımlarken
yıldız işaretini kullanın ve bu işaretçinin dönüş türünün neye işaret ettiğini belirtin.
Bu nedenle, bir işaretçinin döndürülmesiyle ilgili belirli tehlikeler olsa da dikkatli olun.
Argümanın işaret ettiği değişkene müdahale etmekten kaçınmak için yerel değişkenleri kullanın.
İşaretçileri geri döndürürken biraz sorunludur çünkü işlevin içinde işaretçiyi değiştirir
ve sonra geri döndürürseniz, yapmasını
istemediğiniz bir şey olabilir.
Bu nedenle, argümanınızın işaret ettiği bir değişkeni etkilemek istemiyorsanız her zaman
yerel değişkenleri kullanın.
Bu derste, fonksiyonlara nasıl işaretçi
aktarabileceğiniz ve fonksiyonlardan
nasıl işaretçi döndürebileceğiniz anlatıldı.
İşaretçileri bir fonksiyona geçirme mekanizmasını
kullanarak, bir adresi kopyaladığınız için referansla
geçişi gösterir veya taklit eder.
Ve bir adresi kopyaladığınızda, o adrese yazarsanız, o adrese işaret eden
her şeyi güncellersiniz, bu da etkin
bir şekilde fonksiyonun içindeki verinin değerini değiştirdiğiniz
anlamına gelir ve fonksiyonun dışına yansıyacaktır.
Bunu göstermek için takas rutinini kullanacağız.
Bu çok güçlü bir kavramdır. Ve referansla geçişi taklit etmek istiyorsanız, bunu yapmak
için işaretçileri kullanabilirsiniz ve bunu işlevin dışındaki şeyleri değiştirmek istediğinizde yapmak istersiniz.
Teşekkür ederim.