C pointer nedir sorusunun kısa cevabı şudur: Pointer, bir değişkenin değerini değil, bellekte bulunduğu adresi tutan değişkendir. C dilinde bir pointer üzerinden bu adresteki değere ulaşabilir, değeri okuyabilir ve uygun durumlarda değiştirebilirsin.
Bu ilişkiyi anlamanın temel noktası, adres ile değerin iki farklı bilgi olduğudur. Değer, değişkenin taşıdığı içeriktir; adres ise bu içeriğin bellekte bulunduğu konumu gösterir.
C dilinde adres ve değer arasındaki ilişki nedir?
Belleği, üzerinde numaralar bulunan çok sayıda kutunun bulunduğu bir dolap gibi düşünebilirsin. Her kutunun bir adresi, kutunun içinde de bir değer vardır. Örneğin sayi adlı değişkenin değeri 42 olabilir. Bellekte bu değerin bulunduğu kutunun adresi ise program çalışırken belirlenen başka bir bilgidir.
Bu benzetme, adres ile değerin farkını anlamak için kullanışlıdır; ancak bellekteki işlemler fiziksel kutulara veri koymak kadar basit değildir. C dilinde adresler, bellekteki nesnelere erişimi ifade eden teknik bir gösterimdir ve bir pointer’ın türü, o adresteki verinin nasıl yorumlanacağını da belirler. Örneğin int *, bir int değerinin adresini tutmak üzere kullanılır.
Normal bir değişkeni yazdırırken değişkenin değerini elde edersin. Adresini öğrenmek için ise adres alma operatörü olan & kullanılır. Bir pointer, bu adresi saklayabilir.
Adres alma ve pointer tanımlama nasıl yapılır?

Önce bir int değişkeni tanımlayalım. Ardından &sayi ifadesiyle bu değişkenin adresini alıp int *p tanımıyla oluşturduğumuz pointer’a atayalım:
#include <stdio.h>
int main(void) {
int sayi = 42;
int *p = &sayi;
printf("sayi = %dn", sayi);
printf("&sayi = %pn", (void *)&sayi);
printf("p = %pn", (void *)p);
printf("*p = %dn", *p);
return 0;
}
Burada int sayi = 42; ifadesi, değeri 42 olan bir int değişken oluşturur. &sayi, bu değişkenin adresini verir. int *p ise p değişkeninin bir int adresi tutacağını belirtir. Bu nedenle p = &sayi ataması geçerlidir.
*p ifadesi bu örnekte pointer’ın gösterdiği adresteki değeri ifade eder. Program çalıştığında sayi ve *p için 42 yazdırılır. &sayi ile p satırlarında aynı adres görünür; adresin kendisi ise programın her çalıştırılmasında değişebilir. Adres yazdırmak için %p kullanılır ve adres, (void *) dönüşümüyle printf işlevine aktarılır.
Dereference ile değere nasıl ulaşılır ve değer değiştirilir?
Dereference işlemi, bir pointerın tuttuğu adreste bulunan değeri okumayı veya o adresteki değeri değiştirmeyi sağlar. Bunun için ifade içinde pointer adının önüne * yazılır.
#include <stdio.h>
int main(void) {
int sayi = 10;
int *p = &sayi;
printf("Okunan değer: %dn", *p);
*p = 25;
printf("Değişen değer: %dn", sayi);
return 0;
}
Bu örnekte p, sayi değişkeninin adresini tutar. *p ifadesi bu adrese giderek 10 değerini okur. Aynı ifade atamanın sol tarafında kullanıldığında, o adresteki değeri 25 olarak değiştirir. Programın çıktısı şu şekilde olur:
Okunan değer: 10
Değişen değer: 25
- Tanımdaki yıldız:
int *p;ifadesindeki yıldız,pdeğişkeninin pointer olduğunu belirtir. - İfadede kullanılan yıldız:
*pifadesindeki yıldız, pointerın gösterdiği değere erişir. - Atamadaki kullanım:
*p = 25;ifadesi, pointerın gösterdiği değişkenin değerini günceller.
Pointer kullanmadan ve kullanarak takas nasıl yapılır?

İki değişkenin değerini bir fonksiyon içinde kalıcı olarak değiştirmek için fonksiyona bu değişkenlerin adreslerini göndermek gerekir. Pointer kullanılmayan ilk örnekte fonksiyon, değişkenlerin kendisini değil değer kopyalarını alır.
#include <stdio.h>
void takasYanlis(int a, int b) {
int gecici = a;
a = b;
b = gecici;
}
void takas(int *a, int *b) {
int gecici = *a;
*a = *b;
*b = gecici;
}
int main(void) {
int x = 3;
int y = 8;
takasYanlis(x, y);
printf("İlk takastan sonra: x = %d, y = %dn", x, y);
takas(&x, &y);
printf("Pointer ile takastan sonra: x = %d, y = %dn", x, y);
return 0;
}
takasYanlis çağrıldığında x ve y içindeki değerler fonksiyonun a ve b parametrelerine kopyalanır. Fonksiyon bu kopyaları değiştirir, ana fonksiyondaki değişkenlere ulaşamaz. Bu nedenle ilk çıktı x = 3, y = 8 olur.
takas fonksiyonuna &x ve &y ile adresler gönderilir. Fonksiyon içindeki *a ve *b ifadeleri doğrudan asıl değişkenlere eriştiği için değerler kalıcı olarak yer değiştirir. İkinci çıktı x = 8, y = 3 şeklindedir.
Pointer ile dizi arasındaki temel ilişki nedir?
C dilinde dizi ile pointer arasındaki temel ilişki, dizi adının çoğu ifade içinde ilk elemana ait adres gibi kullanılabilmesidir. Bu nedenle bir dizinin başlangıç adresi, uygun türde bir pointer değişkeninde tutulabilir.
Aşağıdaki örnekte p, sayilar dizisinin ilk elemanını gösterir. notlar ifadesi bu kullanımda ¬lar[0] ile aynı başlangıç adresini temsil eder.
#include <stdio.h>
int main(void) {
int notlar[3] = {70, 80, 90};
int *p = notlar;
printf("Ilk eleman: %dn", *p);
printf("Dizi adresi: %pn", (void *)notlar);
printf("Pointer adresi: %pn", (void *)p);
return 0;
}
Bu programda ilk satırda 70 yazdırılır. İki adres satırında görülen değerler, kullanılan sisteme göre değişir ve sabit bir adres beklenmemelidir. Her iki ifade de dizinin ilk elemanının bulunduğu konumu gösterir.
Bu ilişki, dizilerle pointerların tamamen aynı tür olduğu anlamına gelmez. Dizi, elemanları için ayrılmış bir alanı temsil eder; pointer ise bir adresi saklayan değişkendir. Bu bölümde pointer aritmetiğine ve dinamik belleğe girmeden, yalnızca dizinin başlangıç adresinin pointer ile ilişkilendirilmesine odaklanıyoruz.
NULL ve başlatılmamış pointer neden tehlikelidir?
NULL pointer, bilerek geçerli bir nesneyi göstermeyen pointerdır; başlatılmamış pointer ise henüz herhangi bir değer verilmemiş pointerdır. NULL kontrol edilebilir bir durumdur, ancak başlatılmamış pointerın hangi adresi içerdiği belli değildir.
Her iki durumda da pointer üzerinden değere erişmeden önce geçerli bir nesneyi gösterdiğinden emin olmak gerekir. NULL pointerı dereference etmek veya başlatılmamış pointerın gösterdiği yere veri yazmak tanımsız davranışa yol açabilir.
#include <stdio.h>
int main(void) {
int sayi;
int *p = NULL;
scanf("%d", &sayi);
p = &sayi;
*p = 42;
printf("%dn", sayi);
return 0;
}
Bu programda scanf, kullanıcının girdiği değeri sayi değişkeninin adresine yazar. Kullanıcı hangi sayıyı girerse girsin, daha sonra *p = 42 satırı çalıştığı için çıktı 42 olur. p önce NULL olarak başlatılmış, ardından gerçek bir değişkenin adresine yönlendirilmiştir.
scanf("%d", &sayi) kullanımındaki & işareti, sayi değişkeninin adresini verir. Pointer zaten geçerli bir int değişkenini gösteriyorsa scanf("%d", p) biçimi de kullanılabilir. Ancak int *p; şeklinde tanımlanan başlatılmamış bir pointera doğrudan veri yazdırmak güvenli değildir.
Sık Sorulan Sorular
C pointer ile normal değişken arasındaki fark nedir?
Normal değişken doğrudan bir değer saklar, pointer ise başka bir değişkenin bellekteki adresini saklar. Pointerın başındaki dereference yıldızı kullanıldığında, gösterdiği adresteki değere erişilir.
Pointer tanımlarken yıldız ile dereference yıldızı arasındaki fark nedir?
Tanım sırasında yıldız, değişkenin pointer olduğunu belirtir; int *p; ifadesinde p bir int adresi tutar. Kullanım sırasında yıldız, pointerın gösterdiği değere erişir; *p ifadesi bu değeri okur veya değiştirir.
scanf fonksiyonunda neden değişkenin önüne & konur?
scanf girilen değeri değişkene yazabilmek için değişkenin adresine ihtiyaç duyar. Bu nedenle scanf("%d", &sayi) ifadesinde &sayi, sayi değişkeninin adresini fonksiyona gönderir.
NULL pointer ile başlatılmamış pointer aynı mıdır?
NULL pointer ile başlatılmamış pointer aynı değildir. NULL pointer bilinçli olarak geçerli bir nesneyi göstermeyecek şekilde atanır, başlatılmamış pointer ise henüz bir değer almadığı için güvenilir bir adres içermez.
Pointerları güvenli kullanmak için adres ile değeri ayırmalı, her pointerı dereference etmeden önce geçerli bir nesneyi gösterdiğini kontrol etmelisin.