C++’ta referans, var olan bir değişkenin başka bir adıdır; pointer ise bir bellek adresini tutan ayrı bir değişkendir. Referansı bir değişkene bağlayarak onun değerine doğrudan erişebilirsin; pointer kullanırken önce adresi tutar, ardından * işleciyle o adresteki değere erişirsin. C++ referans ve pointer farkı, özellikle fonksiyonlara veri aktarırken ve bir değişkenin başka kodlar tarafından değiştirilmesini isterken önem kazanır.
Referans ile pointer arasındaki temel fark nedir?
Referans bağlandığı nesnenin başka bir adı olarak kullanılır; pointer ise uygun bir adres tutarak o adresteki nesneye erişmeni sağlar. Bellek adresini bir evin adresine benzetebilirsin: Referans aynı ev için kullanılan ikinci bir ad, pointer ise adresin yazılı olduğu ayrı bir not gibidir.
Aşağıdaki programda r, oluşturulurken x değişkenine bağlanır. Ardından r üzerinden yapılan atama, x değerini değiştirir:
#include <iostream>
int main() {
int x = 10;
int& r = x;
r = 25;
std::cout << x << 'n';
}
Programın çıktısı 25 olur. int& r = x ifadesinde r için ayrı bir tam sayı oluşturulmaz; r, x için kullanabileceğin başka bir addır.
Bu örnekteki gibi bir referans değişkeni oluşturulurken bağlanacağı nesne belirtilir ve referans daha sonra başka bir nesneye yeniden bağlanamaz. Örneğin r = y yazmak, r referansını y değişkenine bağlamaz; y değerini r üzerinden x değişkenine atar.
Pointer ise uygun türde başka bir nesnenin adresini alacak şekilde yeniden yönlendirilebilir. Ayrıca pointer nullptr değerini tutabilir; bir referans değişkeni doğrudan nullptr ile başlatılamaz.
Pointer nasıl tanımlanır ve güvenli kullanılır?

Bir tam sayının adresini tutacak pointer, int* p biçiminde tanımlanır. &x ifadesi x değişkeninin adresini alır; *p ise pointer’ın gösterdiği nesnenin değerine erişmek için kullanılır.
Aşağıdaki örnekte p önce x değişkenini, ardından y değişkenini gösterir:
#include <iostream>
int main() {
int x = 10;
int y = 20;
int* p = &x;
if (p != nullptr) {
std::cout << *p << 'n';
}
p = &y;
if (p != nullptr) {
*p = 30;
std::cout << y << 'n';
}
p = nullptr;
if (p != nullptr) {
std::cout << *p << 'n';
}
}
Beklenen çıktı:
10
30
p = &y ataması pointer’ın tuttuğu adresi değiştirir. Sonraki *p = 30 ataması ise bu kez y değişkenini değiştirir. p, nullptr yapıldıktan sonra son koşul sağlanmadığı için gösterilen değere erişme işlemi çalışmaz.
& işaretinin anlamı kullanıldığı yere bağlıdır: int& r = x tanımında referans türünün parçasıdır; &x ifadesinde ise adres alma işlecidir. Benzer şekilde int* p tanımındaki * pointer türünü belirtirken *p ifadesi gösterilen nesneye erişir.
nullptr değerindeki bir pointer üzerinden *p ile nesneye erişmeye çalışmak tanımsız davranışa yol açar. Bununla birlikte nullptr kontrolü tek başına güvenliği kanıtlamaz: Pointer’ın gösterdiği nesnenin yaşam süresinin de devam etmesi gerekir.
std::unique_ptr ve std::shared_ptr ise dinamik olarak oluşturulan nesnelerin sahipliğini yönetmek için kullanılan ayrı akıllı pointer türleridir.
Parametre aktarımında değer, pointer ve referans nasıl ayrılır?

Değerle aktarımda fonksiyon, gönderilen int değişkenlerinin kopyaları üzerinde çalışır. Pointer ile aktarımda değişkenlerin adresleri gönderilir; referansla aktarımda ise parametreler doğrudan çağıranın değişkenlerine bağlanır.
Aşağıdaki örnekte üç fonksiyon da sayıların yerini değiştirmeyi dener. Her çağrıdan önce sayılar yeniden 1 ve 2 değerlerine ayarlanır.
#include <iostream>
void swapByValue(int a, int b) {
int gecici = a;
a = b;
b = gecici;
}
void swapByPointer(int* a, int* b) {
if (a == nullptr || b == nullptr) {
return;
}
int gecici = *a;
*a = *b;
*b = gecici;
}
void swapByReference(int& a, int& b) {
int gecici = a;
a = b;
b = gecici;
}
int main() {
int x = 1;
int y = 2;
swapByValue(x, y);
std::cout << "Degerle: " << x << " " << y << 'n';
x = 1;
y = 2;
swapByPointer(&x, &y);
std::cout << "Pointer ile: " << x << " " << y << 'n';
x = 1;
y = 2;
swapByReference(x, y);
std::cout << "Referansla: " << x << " " << y << 'n';
return 0;
}
Beklenen çıktı şöyledir:
Degerle: 1 2
Pointer ile: 2 1
Referansla: 2 1
swapByValue içindeki değişiklikler kopyalarda kalır. Pointer sürümünde çağrıdaki &x ve &y adresleri alır; fonksiyon içindeki *a ve *b ise bu adreslerdeki değerlere erişir. Kontrol, pointer'lardan biri nullptr ise değerine erişmeden fonksiyondan çıkılmasını sağlar.
Referans sürümünde int& parametrenin referans olduğunu belirtir. Çağrıda ayrıca adres almaya, fonksiyon içinde de * ile değere erişmeye gerek kalmadan asıl değişkenler değiştirilir.
Const referans büyük nesnelerde ne sağlar?
Const referans, büyük bir nesneyi fonksiyona aktarırken nesnenin parametre için kopyalanmasını önlemeye ve fonksiyonun o parametre üzerinden nesneyi değiştirmesini engellemeye yardımcı olur.
#include <iostream>
#include <string>
void yazdir(const std::string& metin) {
std::cout << metin << 'n';
}
int main() {
std::string mesaj = "Merhaba C++";
yazdir(mesaj);
return 0;
}
Bu program Merhaba C++ çıktısını üretir. const, nesnenin her koşulda değişmez olduğunu söylemez; yazdir fonksiyonunun nesneyi metin üzerinden değiştirmesini engeller. Başka bir erişim yolu üzerinden değişiklik yapılabilmesi, asıl nesnenin tanımına ve kullanımına bağlıdır.
Referansın bağlandığı nesnenin yaşam süresi fonksiyon çağrısı boyunca geçerli olmalıdır. Örnekte mesaj, çağrı tamamlanana kadar varlığını sürdürür.
Okuma amaçlı büyük nesneler için const referansı, fonksiyonun çağıranın nesnesini değiştirmesi gerektiğinde normal referansı değerlendirebilirsin. Bir adresin isteğe bağlı olması veya pointer'ın başka bir nesneye yönlendirilmesi gerekiyorsa pointer uygun bir seçenektir.
Referans ve pointer kullanırken hangi hatalardan kaçınılır?
Referans ve pointer kullanırken nesnenin yaşam süresine, & işaretinin bağlamına ve pointer’ın geçerli bir nesneyi gösterip göstermediğine dikkat etmelisin.
Yerel değişkene işaret eden sonuç döndürmek
Fonksiyon içinde tanımlanan otomatik ömürlü yerel bir değişkenin yaşam süresi, bulunduğu blok sona erdiğinde biter. Böyle bir değişkene döndürülen referans veya pointer üzerinden fonksiyon dönüşünden sonra nesneye erişmek tanımsız davranışa yol açar.
Değerin kendisi gerekiyorsa onu doğrudan döndürebilirsin. Çağıran kodun sahip olduğu bir nesneye referans döndürmek de bir seçenektir; ancak o nesnenin referans kullanıldığı sürece yaşamaya devam etmesi gerekir.
& işaretinin görevini karıştırmak
int& r = x; bildiriminde &, r değişkeninin referans olduğunu belirtir. int* p = &x; ifadesinde ise &x, x değişkeninin adresini alır.
İşaretin görevini belirlemek için bulunduğu yere bak: Tür bildiriminin parçası mı, yoksa bir nesnenin adresini alan ifade mi?
Pointer’ı kontrol etmeden kullanmak
Boş olabilen bir pointer’ı * ile kullanmadan önce nullptr kontrolü yapabilirsin. Aşağıdaki örnekte geçerli adres alındığında değer yazdırılır; boş pointer için ayrı bir mesaj gösterilir.
#include <iostream>
void yazdir(const int* p) {
if (p != nullptr) {
std::cout << *p << 'n';
} else {
std::cout << "Adres yokn";
}
}
int main() {
int sayi = 42;
yazdir(&sayi);
yazdir(nullptr);
}
Beklenen çıktı:
42
Adres yok
nullptr kontrolü tek başına yeterli bir geçerlilik kontrolü değildir. Boş olmayan bir pointer da yaşam süresi bitmiş bir nesneyi gösterebilir; bu nedenle gösterilen nesnenin yaşam süresini de takip etmelisin.
Hangi aktarım biçimini seçmelisin?
- Fonksiyonun bağımsız bir kopyayla çalışması yeterliyse değere göre aktarımı değerlendir.
- Çağıranın nesnesini doğrudan değiştirmek gerekiyorsa referans kullanmayı değerlendir.
- Adresin boş olabilmesi veya pointer’ın başka bir nesneye yönlendirilebilmesi gerekiyorsa pointer kullan ve boş olabileceği durumlarda kontrol et.
- Büyük bir nesneyi kopyalamadan, yalnızca okumak için aktarıyorsan
constreferansı değerlendir.
Sık Sorulan Sorular
C++’ta referans nullptr olabilir mi?
C++’ta referansın kendisi nullptr değeri almaz; geçerli bir nesneye bağlanmalıdır. Bununla birlikte, bağlandığı nesnenin yaşam süresi sona ererse referans geçersiz hâle gelebilir.
Pointer ile referans arasında fonksiyon parametresi seçimi nasıl yapılır?
Nesnenin var olması bekleniyorsa referans, nesnenin bulunmaması da geçerli bir durumsa pointer kullanmayı değerlendirebilirsin. Pointer kullandığında boş değer için fonksiyonun nasıl davranacağını belirlemelisin.
const referans ne zaman kullanılmalıdır?
Büyük bir nesneyi kopyalamadan okumak ve fonksiyon içinde o referans üzerinden değiştirmemek istediğinde const referans kullanabilirsin.
Bir fonksiyondan yerel değişkene pointer veya referans döndürmek neden hatalıdır?
Otomatik ömürlü yerel değişkenin yaşam süresi fonksiyon sona erdiğinde bittiği için döndürülen pointer veya referans artık kullanılabilir bir nesneye erişim sağlamaz.
Seçim yaparken nesnenin yaşam süresini ve fonksiyonun o nesne üzerinde ne yapacağını birlikte değerlendirmen, kodun davranışını daha anlaşılır kılar.