C Dilinde Pointer Nedir ve Nasıl Çalışır?
Pointer Temelleri ve Bellek Adresleri
C dilinde pointer (işaretçi), bir değişkenin bellekteki adresini tutan özel bir değişken türüdür. Bellekte her değişkenin belirli bir adresi vardır ve pointer, bu adrese doğrudan erişim sağlar. Örneğin, int türünde bir değişkenin pointer’ı, o değişkenin hafızadaki başlangıç adresidir.
Pointer kullanmak, belleğe düşük seviyede erişim ve kontrol sağlar. Peki, neden adres göndermek önemli?
Diyelim ki fonksiyona bir değişkenin değerini gönderdiniz, fonksiyon bu değerin bir kopyası üzerinde işlem yapar ve asıl değişken etkilenmez. Ancak fonksiyona değişkenin adresini (pointer) verirseniz, fonksiyon verinin kendisi üzerinde işlem yapabilir ve değişikliği kalıcı olur.
Örnekle gösterelim:
#include <stdio.h>
int main() {
int sayi = 10;
int *ptr = &sayi; // sayi değişkeninin adresi ptr’ye atanıyor
printf("Sayi'nin adresi: %p\n", ptr);
printf("Sayi'nin kendisi: %d\n", *ptr); // *ptr ile sayi'nin değeri elde edilir
return 0;
}
Burada &sayi, sayi değişkeninin adresini alır. ptr adresi tutar, *ptr ise adresin gösterdiği yerdeki değeri okur.

Fonksiyonlara Pointer Gönderimi ve Değişkenlerin Manipülasyonu
Neden Adres Gönderelim? Değer Mi, Adres Mi?
C’de fonksiyonlara parametre olarak değişkenlerin değerleri kopyalanarak gönderilir. Bu durumda fonksiyon, dışarıdaki orijinal değişken üzerinde bir şey değiştiremez.
Örnek:
#include <stdio.h>
void degistir(int x) {
x = 100;
}
int main() {
int sayi = 20;
degistir(sayi);
printf("%d\n", sayi); // Çıktı: 20, değişmedi
return 0;
}
Burada degistir fonksiyonuna sayi’nın bir kopyası gönderildiği için fonksiyon içindeki değişiklik kalıcı olmaz.
Peki, orijinal değişkeni nasıl değiştirebiliriz? İşte burada pointer kullanırız.
Pointer ile adres gönderimi:
#include <stdio.h>
void degistir(int *ptr) {
*ptr = 100; // adresin gösterdiği yerdeki değer değiştirilir
}
int main() {
int sayi = 20;
degistir(&sayi); // sayi'nin adresi fonksiyona gönderiliyor
printf("%d\n", sayi); // Çıktı: 100, değişti
return 0;
}
Bu örnekte, degistir fonksiyonu bir pointer alır ve *ptr ile işaretçinin gösterdiği bellek alanındaki değeri değiştirir. Fonksiyona orijinal değişkenin adresi gönderildiği için, yapılan değişiklik doğrudan orijinal sayi üzerine yansır.

Pointer ve Fonksiyonlarda Daha İleri Uygulamalar
Pointer ile Dinamik Bellek Yönetimi ve Fonksiyonlar
Pointer kullanımı fonksiyonlarda sadece değişkenlerin değerini değiştirmekle sınırlı kalmaz. Aynı zamanda dinamik bellek yönetimi için de temel araçtır.
Örneğin, fonksiyon içinde dinamik olarak bellek ayırıp, bunu ana fonksiyona taşımak isteyebiliriz.
#include <stdio.h>
#include <stdlib.h>
void bellekhazirla(int **ptr, int boyut) {
*ptr = (int *)malloc(boyut * sizeof(int)); // belleği ayır
if (*ptr == NULL) {
printf("Bellek ayrılmadı!\n");
exit(1);
}
for (int i = 0; i < boyut; i++) {
(*ptr)[i] = i * 10;
}
}
int main() {
int *dizi = NULL;
int boyut = 5;
bellekhazirla(&dizi, boyut);
for (int i = 0; i < boyut; i++) {
printf("%d ", dizi[i]);
}
free(dizi); // belleği serbest bırakmak önemli
return 0;
}
Bu örnekte, bellekhazirla fonksiyonu çift pointer (int **ptr) alır. Bu sayede main fonksiyonundaki dizi pointer’ının kendisi fonksiyon içinde güncellenebilir. Böylece fonksiyon içinde belleği ayırıp, ana fonksiyonda kullanılabilir hale getiriyoruz.

Çift Pointer Kullanımı ve Dinamik Diziler
Gelişmiş senaryolarda, double pointer kullanımı özellikle dinamik diziler ve çok boyutlu yapılarla uğraşırken büyük kolaylık sağlar.
Çift pointer, pointer’ın kendisini temsil eder yani pointer’a işaret eden pointer’dır. Bu özellikle fonksiyonlar aracılığıyla pointer’ların değiştirilmesi gerektiğinde kullanılır.

Double Pointer Örneği
Aşağıdaki örnek, fonksiyon içinde dinamik olarak iki boyutlu bir diziyi oluşturmayı ve kullanmayı gösterir:
#include <stdio.h>
#include <stdlib.h>
void ikiBoyutluDiziOlustur(int ***dizi, int satir, int sutun) {
*dizi = (int **)malloc(satir * sizeof(int *));
for (int i = 0; i < satir; i++) {
(*dizi)[i] = (int *)malloc(sutun * sizeof(int));
}
for (int i = 0; i < satir; i++) {
for (int j = 0; j < sutun; j++) {
(*dizi)[i][j] = i * sutun + j;
}
}
}
void diziYokEt(int **dizi, int satir) {
for (int i = 0; i < satir; i++) {
free(dizi[i]);
}
free(dizi);
}
int main() {
int **dizi;
int satir = 3, sutun = 4;
ikiBoyutluDiziOlustur(&dizi, satir, sutun);
for (int i = 0; i < satir; i++) {
for (int j = 0; j < sutun; j++) {
printf("%d ", dizi[i][j]);
}
printf("\n");
}
diziYokEt(dizi, satir);
return 0;
}
Bu örnek, çift pointer ile fonksiyonlar arasında pointer değişkenlerinin yönetilmesini ve dinamik olarak oluşturulan iki boyutlu dizileri göstermektedir.
Sonuç
Başlangıç olarak pointer ve fonksiyonların temel etkileşimini anlamak, C dilinde sağlam ve hata yapmayan programlar yazmanın kapısını açar. Bellek adresini yöneten pointer’lar, fonksiyon parametreleriyle birlikte kullanıldığında değişkenlerin değerlerini fonksiyon dışına yansıtmayı sağlar. Bu da programın yönetimini ve performansını etkili şekilde kontrol etmemize olanak verir.
Fonksiyonlarınıza hangi durumlarda değer, hangi durumlarda adres göndermeyi tercih ettiğinizi iyi analiz etmek, programınızın verimliliğini artırır. Dinamik bellek yönetimi, diziler ve kompleks veri yapılarıyla çalışırken pointer’ların gücünü kullanmak olmazsa olmazdır. Bu nedenle pointer’ları etkili ve doğru kullanmak C programcıları için kritik bir beceridir.





