İçeriğe geç

Menü

Berk Akademi
Yorumlar Google yorumları Özel Ders Başvuru Ücretsiz ön görüşme Ana Sayfa

C’de Pointer Nasıl Kullanılır? Adres, * ve & Operatörleri

c-pointer-nasil-kullanilir-adres-ve-operatorleri
Bu yazıda neler var?
  1. Pointer ve bellek adresi nedir?
  2. Pointer nasıl tanımlanır ve kullanılır?
  3. Fonksiyona pointer göndererek iki sayı nasıl değiştirilir?
  4. scanf kullanırken neden adres verilir?
  5. NULL ve ilk değer verilmemiş pointer nasıl yönetilir?
  6. Pointer hatalarını nasıl kontrol edebilirsin?
  7. Sık Sorulan Sorular

C’de pointer kullanmak, bir değişkenin değerini doğrudan taşımak yerine o değişkenin bellekteki adresi üzerinden çalışmayı sağlar. & operatörü bir nesnenin adresini alır, * operatörü ise pointer’ın gösterdiği adresteki değere erişir. Bu iki operatörün ilişkisini kavradığında pointer kullanımı daha anlaşılır hâle gelir.

Pointer’lar özellikle fonksiyonlara değişkenleri değiştirme imkânı vermek, scanf ile veri almak ve bellek üzerinde kontrollü çalışmak için kullanılır. Önce bir değişkenin değeri ile adresi arasındaki farkı ayırmak gerekir.

Pointer ve bellek adresi nedir?

Pointer, uygun türde bir nesnenin bellekteki adresini saklayan değişkendir. Örneğin int türündeki sayi değişkeni bellekte bir değer tutar. Bu değerin bellekte bulunduğu konumu öğrenmek için &sayi ifadesini kullanırsın. C standardına göre & operatörü, uygulandığı nesnenin adresini pointer olarak üretir.

Bunu bir kutu ve kutunun üzerindeki konum etiketi gibi düşünebilirsin. Kutunun içindeki sayı değişkenin değeridir, etiket ise bu kutuya ulaşmak için kullanılan adrestir. Benzetmenin sınırı şudur: pointer da kendi başına bir değişkendir ve içinde adres değerini saklar; adres, program boyunca sabit kalacağı garanti edilen sıradan bir sayı gibi ele alınmamalıdır.

#include <stdio.h>

int main(void) {
    int sayi = 42;

    printf("deger: %dn", sayi);
    printf("adres: %pn", (void *)&sayi);

    return 0;
}

Bu programın çıktısı şu biçimde görünür:

deger: 42
adres: 0x7ffd1234abcd

Buradaki adres yalnızca örnek biçimi gösterir. Farklı çalıştırmalarda farklı bir adres görmen mümkündür. %p biçim belirteci pointer değerini yazdırır ve C standardı bu değerin ekranda nasıl bir karakter dizisine dönüştürüleceğini uygulamaya bırakır. printf çağrısında (void *) dönüşümünü kullanmak, nesne pointer’ını void * türüne dönüştürerek bu biçim belirteciyle uyumlu bir kullanım sağlar.

Pointer nasıl tanımlanır ve kullanılır?

Pointer nasıl tanımlanır ve kullanılır?

Bir pointer tanımlamak için önce göstereceği nesnenin türünü, ardından pointer adını ve yıldız işaretini yazarsın. int *p; ifadesi, int türündeki bir nesnenin adresini tutabilecek p adlı pointer’ı tanımlar.

#include <stdio.h>

int main(void) {
    int sayi = 10;
    int *p;

    p = &sayi;

    printf("Baslangic: %dn", *p);

    *p = 25;

    printf("Degisen: %dn", sayi);

    return 0;
}

Programın sayısal çıktısı şöyledir:

Baslangic: 10
Degisen: 25

Bu örnekte p = &sayi; ataması, p pointer’ına sayi değişkeninin adresini verir. *p ifadesi bu adresin gösterdiği nesneye erişir. Bu nedenle ilk printf çağrısı sayi değerini okur, *p = 25; satırı ise aynı değişkenin değerini değiştirir.

* işaretinin iki kullanımını birbirinden ayırmalısın. int *p; tanımında yıldız, p değişkeninin pointer türünde olduğunu belirtir. *p ifadesinde ise yıldız, pointer’ın gösterdiği değere erişir. Aynı sembol kullanılsa da biri tanımın tür bilgisini, diğeri ifadede yapılan erişimi gösterir.

int *p; yazmak pointer’a otomatik olarak geçerli bir adres vermez. Böyle bir pointer’a uygun bir adres atamadan *p üzerinden okuma yapmak veya yazma yapmak tanımsız davranışa yol açabilir. Bu yüzden pointer’ı kullanmadan önce onu geçerli bir nesnenin adresine, uygun bir pointer değerine ya da sonraki bölümlerde ele alınacak şekilde NULL değerine açıkça başlatmalısın. Geçersiz bir pointer üzerinden dolaylı erişimin tanımsız davranış oluşturabileceği C standardında belirtilir.

Fonksiyona pointer göndererek iki sayı nasıl değiştirilir?

Bir fonksiyonun çağıran fonksiyondaki iki int değişkeni değiştirmesi için bu değişkenlerin adreslerini pointer parametreleriyle göndermen gerekir. Çünkü C’de fonksiyon argümanları değerle aktarılır. Pointer gönderdiğinde de pointer değerinin kendisi kopyalanır, ancak kopyalanan adres üzerinden asıl değişkene erişilebilir.

Önce pointer kullanmadan yapılan yer değiştirme işlemine bakalım:

#include <stdio.h>

void swap_deger(int a, int b) {
    int gecici = a;
    a = b;
    b = gecici;

    printf("Fonksiyon icinde: %d %dn", a, b);
}

int main(void) {
    int x = 10;
    int y = 20;

    swap_deger(x, y);

    printf("Cagiran fonksiyonda: %d %dn", x, y);
    return 0;
}

Bu programda fonksiyon içinde 10 ve 20 yer değiştirir. Çağıran fonksiyondaki değişkenler ise aynı kalır.

Fonksiyon icinde: 20 10
Cagiran fonksiyonda: 10 20

swap_deger fonksiyonundaki a ve b, x ve y değişkenlerinin kopyalarıdır. Bu nedenle kopyalar üzerinde yapılan değişiklikler asıl değişkenlere yansımaz.

Asıl değişkenleri değiştirmek için pointer parametreleri kullanılır:

#include <stdio.h>

void swap_pointer(int *a, int *b) {
    int gecici = *a;
    *a = *b;
    *b = gecici;
}

int main(void) {
    int x = 10;
    int y = 20;

    printf("Adresler: %p, %pn", (void *)&x, (void *)&y);

    swap_pointer(&x, &y);

    printf("Degerler: %d %dn", x, y);
    return 0;
}

&x ve &y ifadeleri değişkenlerin adreslerini gönderir. Fonksiyon içindeki *a ve *b ifadeleri ise bu adreslerde bulunan değerlere erişir. Böylece işlem doğrudan x ve y üzerinde yapılır.

Adresler: 0x7ffd1234abcd, 0x7ffd1234abe0
Degerler: 20 10

Adresteki hexadecimal değerler yalnızca örnektir ve her çalıştırmada değişebilir. %p ile adres yazdırırken (void *) dönüşümü kullanmak, C standardındaki biçimlendirilmiş çıktı kurallarına uygun bir kullanımdır.

scanf kullanırken neden adres verilir?

scanf kullanırken neden adres verilir?

scanf, klavyeden okuduğu değeri bir değişkene yazabilmek için o değişkenin adresini ister. %d dönüşüm belirteci kullanıldığında karşılık gelen argüman int türündeki bir değişkeni gösteren pointer olmalıdır.

#include <stdio.h>

int main(void) {
    int sayi;

    if (scanf("%d", &sayi) == 1) {
        printf("Okunan sayi: %dn", sayi);
    } else {
        printf("Gecersiz girdi.n");
    }

    return 0;
}

Örnek girdi 42 olduğunda çıktı şöyledir:

Okunan sayi: 42

scanf dönüş değeri, başarıyla atanan değerlerin sayısını gösterir. Bu örnekte bir değer okunup sayi değişkenine atanırsa dönüş değeri 1 olur. & işaretini unutup scanf("%d", sayi) yazmak, fonksiyona adres yerine sayının değerini gönderir. Fonksiyon bu değeri adres gibi kullanmaya çalışacağından programda tanımsız davranış oluşabilir.

Karakter dizilerinde kullanım biraz farklı görünür:

#include <stdio.h>

int main(void) {
    char ad[20];

    if (scanf("%19s", ad) == 1) {
        printf("Merhaba, %sn", ad);
    }

    return 0;
}

ad ifadesi, bu kullanımda dizinin ilk elemanını gösteren char * pointer'a dönüşür. &ad ise bütün diziyi gösteren char (*)[20] türündedir ve %s için uygun argüman değildir. %19s kullanımı, char ad[20] dizisindeki sonlandırıcı '' karakteri için yer bırakır.

NULL ve ilk değer verilmemiş pointer nasıl yönetilir?

NULL atanmış bir pointer geçerli bir nesneyi göstermez. Bu nedenle pointer üzerinden * ile değere erişmeden önce NULL kontrolü yapılmalı ve pointer geçerli bir adresi gösterecek şekilde atanmalıdır. C standardındaki adres alma ve dereference kuralları için WG14 pointer tanımları incelenebilir.

İlk değer verilmemiş yerel pointer ile NULL atanmış pointer aynı şey değildir. int *ilk_deger_yok; ifadesinde pointer'a henüz güvenli bir değer atanmaz. Böyle bir pointer'ı okumak, karşılaştırmak veya dereference etmek yerine önce geçerli bir adres ya da NULL atamak gerekir. Güvenli başlangıç için int *p = NULL; yazabilirsin.

#include <stdio.h>
#include <stddef.h>

int main(void)
{
    int sayi = 42;
    int *bos = NULL;
    int *gecerli = &sayi;

    if (bos == NULL) {
        printf("bos pointer deger gostermiyorn");
    }

    if (gecerli != NULL) {
        printf("Deger: %dn", *gecerli);
    }

    return 0;
}

Programın çıktısı şu iki satır olur:

bos pointer deger gostermiyor
Deger: 42

bos için NULL kontrolü yapılmadan *bos yazılsaydı, geçerli bir nesneye erişim gerçekleşmezdi. NULL kontrolü önemli bir ilk adımdır ancak bütün geçersiz pointer durumlarını yakalamaz. Nesnenin ömrü sona erdikten sonra kullanılan pointer'lar, dizi sınırlarının dışındaki adresler veya yanlış türle kullanılan adresler NULL olmayabilir.

Pointer hatalarını nasıl kontrol edebilirsin?

Pointer hatalarını azaltmak için kodu çalıştırmadan önce türü, başlangıç değeri, adresin kullanım süresi ve * operatörünün uygulandığı yeri birlikte kontrol etmelisin. NULL olmayan her adresin otomatik olarak güvenli kabul edilmesi hatalı sonuçlara yol açabilir.

  • Pointer türünün gösterdiği veriyle uyumlu olduğunu kontrol et. int * bir int nesnesini, char * ise karakter verisini göstermelidir.
  • Yerel pointer'ları tanımlarken ilk değer ver. Henüz kullanılmayacaksa NULL, kullanılacaksa geçerli bir nesnenin adresini ata.
  • *p ifadesinden önce p'nin geçerli bir nesneyi gösterdiğinden ve o nesnenin hâlâ kullanımda olduğundan emin ol.
  • scanf çağrılarında veri türüne uygun adresi gönder. Örneğin int sayi için scanf("%d", &sayi) kullanılır.
  • Pointer adresini yazdırırken %p dönüşümünü ve (void *) dönüşümünü kullan: printf("%pn", (void *)p).

GCC ile derlerken uyarıları etkinleştirmek için şu komutu kullanabilirsin:

gcc -Wall -Wextra -pedantic pointer.c -o pointer

-Wall çeşitli yararlı uyarıları, -Wextra ek uyarıları etkinleştirir. -pedantic ise standart dışı kullanımlar konusunda uyarı üretmeye yardımcı olur. Bu seçeneklerin açıklamaları GCC uyarı ve hata belgelerinde yer alır.

Derleyicinin uyarı vermemesi, programın bütün pointer hatalarından arındığını kanıtlamaz. Bir pointer'ın yanlış nesneyi göstermesi, nesnenin kullanım süresinin bitmesi veya program mantığının yanlış kurulması her zaman derleyici tarafından yakalanmayabilir. Pointer aritmetiği ve dinamik bellek kullanımı, bu temel kontrollerden sonra ayrıca ele alınması gereken konulardır.

Sık Sorulan Sorular

C'de & ve * operatörleri arasındaki fark nedir?

& operatörü bir değişkenin adresini alır. * operatörü ise bir pointer'ın gösterdiği adresteki değere erişir. Örneğin int *p = &sayi; satırında &sayi adresi üretir, *p ise bu adreste bulunan sayıyı ifade eder.

NULL pointer üzerinden değere erişilirse ne olur?

NULL pointer üzerinden *p ile değere erişmek geçerli bir nesneye erişim sağlamaz ve C açısından tanımsız davranışa yol açar. Program sonlanabilir, hatalı sonuç üretebilir veya farklı bir davranış gösterebilir. Bu yüzden dereference işleminden önce pointer'ın geçerli biçimde atanması gerekir.

scanf ile karakter dizisi okurken neden dizi adının başına & konmaz?

Karakter dizisinin adı çoğu ifade bağlamında dizinin ilk elemanını gösteren pointer'a dönüşür. Bu nedenle char ad[30]; tanımından sonra scanf("%29s", ad) yazılır. &ad ise dizinin tamamına pointer anlamına gelir ve %s dönüşümünün beklediği türle aynı değildir.

Pointer kullanırken güvenli yaklaşım, her adresi türüyle ve kullanım amacıyla birlikte değerlendirmektir. NULL kontrolü, doğru dereference ve derleyici uyarıları bu kontrolün temel parçalarını oluşturur.

Bu içerik aradığın cevabı verdi mi?
Yanıtın, hangi yazıları geliştirmemiz gerektiğini anlamamıza yardımcı olur.
Bu içeriğin üretilmesinde yapay zeka araçlarından destek alınmıştır.

Bu konudan sonra ne okuyabilirsin?

Tüm yazılar

İlgili Eğitimler

Berk Keskin, yazılım geliştirici ve eğitmen
Yazar

Berk Keskin Kimdir?

Yazılıma 12 yaşında başladı; İzmir Ekonomi Üniversitesi'ni bölüm birincisi ve yüksek şeref öğrencisi olarak tamamladı. Bugün yalnızca eğitim vermekle kalmıyor, sektörde aktif olarak yazılım projeleri geliştiriyor ve gerçek dünya deneyimini birebir derslerine taşıyor. Ezberden uzak, mühendislik zihniyetini merkeze alan sürdürülebilir öğrenme sistemleri tasarlayarak sorgulayan, üreten ve problem çözebilen yeni nesil yazılımcılar yetiştiriyor.

Sektörel Deneyim & Projeler

  • Ticarify Entegrasyon Yazılım logosu CEO Ticarify Entegrasyon YazılımPazaryerleri ve e-ticaret sitelerine otomatik e-fatura kesimi, sipariş ve kargo takibi hizmetleri sunan e-Dönüşüm platformunun API mimarisini ve yazılım ekibini yönetmektedir.
  • Benim Düğünüm logosu CEO Benim DüğünümDijital etkinlik ve anı paylaşım platformu.
  • Siberdizayn logosu Yazılım Ekibi Lideri SiberdizaynYüksek anlık oyuncu trafiğine sahip oyun kontrol panelleri ve sunucu altyapıları geliştiren yazılım ekibine liderlik etmektedir.
  • MEDYOGRAFYA 360° Dijital Çözümler logosu Dijital Strateji Lideri MEDYOGRAFYA 360° Dijital ÇözümlerŞirketlerin dijital çözümlerde uzun vadede nasıl ilerlemesi gerektiği ve dijital dönüşüm süreçlerinin yönetilmesine destek olmaktadır.
  • İzmir Ekonomi Üniversitesi logosu Danışma Kurulu Üyesi İzmir Ekonomi ÜniversitesiMezun olduğu üniversitesinde, Bilgisayar Programcılığı bölümünün akademik müfredatını güncel sektör ihtiyaçlarına göre şekillendirmek adına Danışma Kurulu'nda görev almaktadır.
WhatsApp Hemen Ara