Programlama Temelleri

9 - İşaretçiler (Pointers)

Emre Can Yılmaz

Ondokuz Mayıs Üniversitesi

2024

İşaretçiler: Belleğin Adres Defteri

  • İşaretçiler (pointers), bellekteki bir değişkenin adresini tutan değişkenlerdir.
  • Normal değişkenler değer saklarken, işaretçiler adres saklar.
  • İşaretçiler sayesinde, bir değişkenin değerini dolaylı olarak değiştirebilir, fonksiyonlara veri gönderebilir ve dinamik bellek yönetimi gibi işlemleri gerçekleştirebiliriz.

İşaretçiler ve Bellek: Görsel Bir Açıklama

  • Bilgisayarın belleği, ardışık olarak numaralandırılmış, her biri bir bayt (byte) veri saklayabilen küçük bölmelerden oluşur. Her bölmenin benzersiz bir adresi ve bir değeri vardır.
+------------+------------+------------+------------+
|    Adres    |   0x1000  |   0x1004  |   0x1008    | ...
+------------+------------+------------+------------+
|    Değer   |     10     |   0x1000   |     'A'    | ...
+------------+------------+------------+------------+
                   ^             ^
                   |             |
                int sayi       int *ptr
  • sayi değişkeni 0x1000 adresinde saklanıyor ve değeri 10.
  • ptr işaretçisi 0x1004 adresinde saklanıyor ve değeri sayi değişkeninin adresi olan 0x1000.
  • *ptr ifadesi, ptr’nin tuttuğu adresteki değeri verir (burada, 10). * operatörüne, bu bağlamda, dereference operatörü denir.

İşaretçi Tanımlama ve İlklendirme

Sözdizimi:

veri_tipi *isaretci_adi;
  • veri_tipi: İşaretçinin işaret edeceği değişkenin veri tipi.
  • *: İşaretçi olduğunu belirtir.
  • isaretci_adi: İşaretçiye verilen isim.

İlklendirme:

isaretci_adi = &değişken_adı; // &: Adres operatörü

Örnek

#include <stdio.h>

int main() {
  int sayi = 10;
  int *ptr; // int türünde bir işaretçi tanımlama

  ptr = &sayi; // ptr'ye sayi'nin adresi atanır

  printf("sayi'nin değeri: %d\n", sayi);
  printf("sayi'nin adresi: %p\n", &sayi);
  printf("ptr'nin değeri: %p\n", ptr); // ptr, sayi'nin adresini tutar
  printf("ptr'nin gösterdiği adresteki değer: %d\n", *ptr); // *ptr, sayi'nin değerini verir

  return 0;
}

İşaretçilerle Değerleri Değiştirme

  • İşaretçiler aracılığıyla, işaret ettikleri değişkenin değerini değiştirebiliriz.

Örnek

#include <stdio.h>

int main() {
  int sayi = 10;
  int *ptr = &sayi;

  *ptr = 20; // ptr'nin gösterdiği adresteki değer (sayi) 20 olur

  printf("sayi'nin yeni değeri: %d\n", sayi); // Çıktı: 20
  return 0;
}

İşaretçiler ve Fonksiyonlar

  • İşaretçiler, fonksiyonlara parametre olarak geçirilebilir.
  • Bu sayede, fonksiyonlar, ana programdaki değişkenlerin değerlerini değiştirebilir.

Örnek: İki Sayıyı Değiştirme

#include <stdio.h>

void degistir(int *a, int *b) {
  int temp = *a;
  *a = *b;
  *b = temp;
}

int main() {
  int sayi1 = 10;
  int sayi2 = 20;

  printf("Önce: sayi1 = %d, sayi2 = %d\n", sayi1, sayi2);

  degistir(&sayi1, &sayi2);

  printf("Sonra: sayi1 = %d, sayi2 = %d\n", sayi1, sayi2);
  return 0;
}

İşaretçiler ve Diziler

  • Bir dizinin adı, dizinin ilk elemanının bellek adresini temsil eden bir işaretçidir.

Örnek

#include <stdio.h>

int main() {
  int sayilar[5] = {10, 20, 30, 40, 50};
  int *ptr = sayilar; // ptr, sayilar dizisinin ilk elemanının adresini gösterir

  printf("İlk eleman: %d\n", *ptr);        // Çıktı: 10
  printf("İkinci eleman: %d\n", *(ptr + 1)); // Çıktı: 20
  printf("Üçüncü eleman: %d\n", *(ptr + 2));// Çıktı: 30
  return 0;
}

NULL Pointer

  • NULL, bir işaretçinin hiçbir adresi işaret etmediğini belirtmek için kullanılan özel bir değerdir.
  • stdio.h başlık dosyasında tanımlıdır.
  • Değeri 0’dır.

Örnek

#include <stdio.h>

int main() {
  int x, *p1, *p2, *p3;
  x = 123;
  p2 = NULL;
  p3 = &x;

  printf("Değer atanmamış p1 pointer'ının değeri: %p\n", p1);
  printf("p2 = NULL atanmış pointer'ın değeri: %p\n", p2);
  printf("p3 = &x atanmış pointer'ının değeri: %p\n", p3);
  printf("&x adresinin değeri: %p\n", &x);

  if (p2 == NULL) {
    printf("p2 bir null pointer'dır. Herhangi bir adresi göstermiyor.\n");
  }

  if (p3 != NULL) {
    *p3 = 5; // p3, x'in adresini gösterdiği için x'in değerini değiştirebiliriz.
    printf("x'in yeni değeri (p3 ile değiştirildi): %d\n", x);
  }

  return 0;
}

İşaretçilerin Avantajları

  • Dinamik Bellek Yönetimi: Programın çalışma zamanında bellek alanlarını dinamik olarak ayırmamızı ve yönetmemizi sağlar. (İlerleyen bölümlerde detaylı olarak işlenecek.)
  • Fonksiyon Parametreleri: Fonksiyonlara büyük veri yapıları geçirmeden, sadece adreslerini geçirerek performansı artırır ve bellek kullanımını azaltır.
  • Veri Yapıları: İşaretçiler, bağlı listeler, ağaçlar gibi karmaşık veri yapıları oluşturmak için kullanılır. (İlerleyen bölümlerde detaylı olarak işlenecek.)

İşaretçilerle Çalışırken Dikkat Edilmesi Gerekenler

  • İşaretçileri İlklendirme: Kullanmadan önce işaretçilere bir adres veya NULL değeri atamak önemlidir.
  • Bellek Sızıntıları: Dinamik olarak ayrılan bellek alanları, kullanıldıktan sonra serbest bırakılmalıdır (free() fonksiyonu ile). (İlerleyen bölümlerde detaylı olarak işlenecek.)

Alıştırma Soruları

  1. İki tamsayı değişkeni tanımlayın ve bu değişkenlere değer atayın. Daha sonra, bu değişkenlerin bellek adreslerini ekrana yazdıran bir program yazın.
  2. Bir tamsayı değişkeni ve bir işaretçi tanımlayın. İşaretçiyi, tamsayı değişkeninin adresine eşitleyin. İşaretçi aracılığıyla tamsayı değişkeninin değerini değiştirin ve ekrana yazdırın.
  3. Bir tamsayı dizisi tanımlayın ve bu dizinin elemanlarını bir işaretçi kullanarak ekrana yazdıran bir program yazın. Dizinin elemanlarına hem dizi notasyonu (dizi[i]) hem de işaretçi notasyonu (*(ptr+i)) kullanarak erişimi gösterin.
  4. Bir fonksiyon yazın. Bu fonksiyon, bir tamsayı işaretçisi ve bir tamsayı değeri alsın. Fonksiyon, işaretçinin gösterdiği adresteki değeri, verilen tamsayı değeri kadar artırsın. main fonksiyonunda bu fonksiyonu farklı değerlerle çağırarak test edin.

Özet

  • İşaretçiler: Bellekteki değişkenlerin adreslerini tutan değişkenlerdir.
  • * operatörü : İşaretçinin gösterdiği adresteki değere erişir.
  • & (Adres Operatörü): Değişkenin bellek adresini verir.
  • NULL Pointer: Hiçbir adresi göstermeyen işaretçi.
  • İşaretçiler ve Diziler: Dizi isimleri, işaretçi gibidir.
  • İşaretçiler ve Fonksiyonlar: İşaretçiler, fonksiyonlara parametre olarak geçirilebilir.

Gelecek Hafta

  • Yapılar (Structures)