Menu

C++ İşaretçiler: Adres Alma, Dereference ve nullptr

C++ işaretçileri sıfırdan anlatılıyor: bir işaretçi tanımlamak, & (adresini al) ve * (dereference) operatörleri, nullptr, dizilere işaretçiler ve çökmelere yol açan sarkan ve ilklendirilmemiş işaretçi tuzakları.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Bir Adres Tutan Değişken

Her değişken bellekte bir yerde, adres denilen numaralı bir konumda yaşar. Çoğu zaman bunun nerede olduğu umurunuzda olmaz; sadece değişkenin adını kullanırsınız. Bir işaretçi bunu tersine çevirir: değeri bir adres olan bir değişkendir. 42 tutmak yerine, "42'nin saklandığı yeri" tutar.

İşaretçileri güçlü kılan bu dolaylılıktır. Fonksiyonlar bir işaretçi aracılığıyla çağıranın değişkenini değiştirebilir, bağlı listeler gibi veri yapıları düğümleri onlarla birbirine zincirler ve (dinamik bellek bölümünde göreceğiniz gibi) çalışma zamanında ayırdığınız belleğe onlarla erişirsiniz.

&score içindeki &, adresini al operatörüdür; score'un konumunu üretir. *p içindeki *, dereference operatörüdür; adresi takip ederek orada yaşayan değere geri döner.

İki Operatör: & ve *

Yeni başlayanlar için en kafa karıştırıcı şey, *'ın bulunduğu yere göre iki farklı anlama gelmesidir. Bunları net tutun:

int* p;     // TANIMLAMA: "p, int'e bir işaretçidir"
p = &x;     // & = adresini al: x'in adresini p'de sakla
int y = *p; // * = dereference: p'nin işaret ettiği değeri oku
*p = 99;    // sol tarafta dereference: işaretçi üzerinden yaz

Bir tanımlamada * türün bir parçasıdır. Bir ifadede * iş yapar. Bir işaretçi kurulduktan sonra, onu dereference etmek size orijinal değişkene tam okuma/yazma erişimi verir:

  1. satırdan sonra health'e adıyla hiç dokunmadığınızı, ama değerinin değişmeye devam ettiğini fark edin. Bütün mesele bu: hp, aynı belleğe bir takma addır. Boşluk kullanımı (int* p, int *p, int*p) sadece görseldir ve derleyici için aynıdır; bu rehber int* p kullanır.

nullptr: Hiçbir Şeye İşaret Etmek

Hiçbir yere işaret etmeyen bir işaretçi nullptr (C++11) olarak ayarlanmalıdır. "Henüz hedef yok" demenin açık ve tür güvenli bir yoludur ve dereference etmeden önce kontrol edebileceğiniz bir şey verir.

Eski NULL makrosuna ya da çıplak bir 0'a kıyasla nullptr'ı tercih edin. nullptr gerçek bir işaretçi türüne sahip olduğundan, aşırı yükleme çözümlemesi sırasında asla tamsayı 0 olarak yanlış okunmaz; eski tarzın yol açabileceği ince bir hata.

Tuzak: null dereference. Bir null (veya ilklendirilmemiş) işaretçi üzerinden okumak ya da yazmak tanımsız davranıştır, genellikle anında bir çökmedir:

int* p = nullptr;
cout << *p;   // ÇÖKME - null'u dereference etmek tanımsız davranıştır

Null olabilecek herhangi bir şeyi dereference etmeden önce her zaman if (p) (veya if (p != nullptr)) ile koruma altına alın.

İşaretçiler ve Diziler

Bir dizinin adı, ilk elemanına bir işaretçiye dönüşür (decay), bu yüzden işaretçiler ve diziler derinden iç içedir. Bir işaretçiye 1 eklemek bir bayt eklemez; bir eleman ilerletir ve işaretçi aritmetiğini çalıştıran şey budur:

p[i] ve *(p + i) tam anlamıyla aynı ifadedir; bu denklik, dizilerin sıfırdan indekslenmesinin nedenidir. Buradaki klasik hata sonunu geçmektir: nums + 4, karşılaştırmak için geçerli bir son-bir-ötesi işaretidir, ama *(nums + 4)'ü dereference etmek sınır dışını okur. İşaretçilerle bir-eksik/bir-fazla hataları çökmelerin ve sessiz bozulmaların başlıca nedenlerindendir, bu yüzden durma koşulunuzda dikkatli olun.

const ve İşaretçiler

const, işaretçinin işaret ettiği şeye, işaretçinin kendisine ya da her ikisine birden uygulanabilir. Çözmek için tanımlamayı sağdan sola okuyun:

const int* p;        // const int'e işaretçi  - *p değiştirilemez, p yeniden yönlendirilebilir
int* const p = &x;   // int'e const işaretçi  - *p değiştirilebilir, p yeniden yönlendirilemez
const int* const p = &x; // ikisi de kilitli

Bu, gerçek kodda sürekli önemlidir. Verinizi değiştirmeyeceğine söz veren bir fonksiyon, const'a bir işaretçi alır:

İşaret edileni const işaretlemek niyeti belgeler ve derleyicinin kazara yazmaları durdurmasını sağlar; çalışma zamanında sıfır maliyetle bedava güvenlik.

Büyük Tuzak: Sarkan İşaretçiler

Bir sarkan işaretçi, artık beklediğiniz değeri tutmayan belleğe işaret eder; değişken kapsam dışına çıkmıştır ya da bellek serbest bırakılmıştır. Onu dereference etmek tanımsız davranıştır ve kötü yanı, çalışmayı bırakana kadar genellikle çalışıyormuş gibi görünmesidir.

int* makeBad() {
    int local = 5;
    return &local;   // HATA: fonksiyon döndüğünde local ölür
}                    // döndürülen işaretçi artık sarkıyor

Adres hâlâ geçerli bir sayıdır, ama geri kazanılmış bir yığın (stack) yuvasına işaret eder; onu okumak çöp verir ya da çökertir. Aynı şey, delete edilmiş bir öbek (heap) nesnesine ya da sonradan yeniden tahsis eden bir vector'ün bir elemanına işaretçi tuttuğunuzda da olur.

Üç kural sizi güvende tutar:

  • Asla bir yerel değişkenin adresini döndürmeyin. Değer olarak döndürün ya da belleği çağıranın sahiplenmesini sağlayın.
  • İşaret ettiği şey yok olduktan sonra bir işaretçiyi nullptr yapın ve kullanmadan önce kontrol edin.
  • Sahiplik ve yaşam süreleri için, çıplak new/delete yerine akıllı işaretçilere başvurun; belleği otomatik olarak serbest bırakırlar ve bu tüm hata sınıfını küçültürler.

Sıradaki: Referanslar ve İşaretçiler

İşaretçiler, başka bir değişkene dolaylı olarak başvurmanın tek yolu değildir. C++'ta ayrıca referanslar vardır; benzer hissettirirler ama null olamazlar, yeniden bağlanamazlar ve daha temiz bir söz dizimi kullanırlar. Sırada hangi aracı seçeceğinizi tam olarak bilmeniz ve modern C++'ın çoğunun mümkün olduğunda neden referansları tercih ettiğini anlamanız için onları referanslar ve işaretçiler bölümünde yan yana koyacağız.

Sıkça Sorulan Sorular

C++'ta işaretçi nedir?

Bir işaretçi, başka bir değerin kendisi yerine onun bellek adresini saklayan bir değişkendir. Onu * ile tanımlarsınız (örneğin int* p), & operatörüyle bir adres alırsınız (p = &x) ve *p ile dereference ederek işaret ettiği değeri okur ya da yazarsınız.

C++ işaretçilerinde & ile * arasındaki fark nedir?

İşaretçi bağlamında &, adresini al operatörüdür; &x size x'in adresini verir. * ise iki iş yapar: bir tanımlamada (int* p) değişkeni işaretçi olarak işaretler, bir ifadede (*p) ise o adreste saklanan değere ulaşmak için işaretçiyi dereference eder.

C++'ta nullptr nedir ve neden NULL yerine onu kullanmalı?

nullptr, C++11 ile eklenen, tür güvenli bir boş işaretçi sabitidir. "Hiçbir şeye işaret etmiyor" anlamına gelir. Eski NULL ya da çıplak 0 yerine onu tercih edin çünkü nullptr gerçek bir işaretçi türüne sahiptir, bu yüzden aşırı yükleme çözümlemesinde asla tamsayı sanılmaz. Dereference etmeden önce her zaman if (p) ile kontrol edin; boş bir işaretçiyi dereference etmek tanımsız davranıştır.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA