Gösterici Kavramı (Pointer)

C Programlama Dilinde Pointer(Yer Gösterici) Kavramı

Merhaba kodlamazamani.blogspot.com.tr takipçileri. Programlama dilinde pointer kavramı oldukça önemli bir değere sahiptir.

Sizi yormayayım diye paylaştığım bu konu anlatımının kaynağı işte tam da bu TIK'ın altında!!




Giriş
Hemen hemen bütün programlama dillerinin temelinde gösterici (pointer) veri tipi bulunmaktadır. Bir çok dil gösterici kullanımını kullanıcıya sunmamıştır veya çok sınırlı olarak sunmuştur. Fakat C Progrmalama Dili'nde göstericiler yoğun olarak kullanılır. Hatta gösterici kavramı C dilinin bel kemiğidir. Kavranması biraz güç olan göstericiler için -latife yapılıp- C kullanıcılarını "gösterici kullanabilenler ve kullanmayanlar" olmak üzere iki gruba ayıranlar da olmuştur. Özetle, bir C programcısı gösterici kavramını anlamadan C diline hakim olamaz.
Türkçe yazılan C kitaplarda pointer kelimesi yerine aşağıdaki ifadelerden biri karşılaşılabilir:
pointer = işaretçi = gösterici = gösterge
Anlatımda, gösterici terimini kullanacağız.

Değişken ve Bellek Adresi
Bilgisayarın ana belleği (RAM) sıralı kaydetme gözlerinden oluşmuştur. Her göze bir adres atanmıştır. Bu adreslerin değerleri 0 ila belleğin sahip olduğu üst değere bağlı olarak değişebilir. Örneğin 1GB MB bir bellek, 1024*1024*1024 = 1073741824 adet gözden oluşur. Değişken tiplerinin bellekte işgal ettiği alanın bayt cinsinden uzunluğu sizeof() operatörüyle öğrenildiğini hatırlayın. (bkz: Program 2.1).
Bir programlama dillinde, belli bir tipte değişken tanımlanıp ve bir değer atandığında, o değişkene dört temel özellik eşlik eder:
  1. değişkenin adı
  2. değişkenin tipi
  3. değişkenin sahip olduğu değer (içerik)
  4. değişkenin bellekteki adresi
Örneğin tam adlı bir tamsayı değişkenini aşağıdaki gibi tanımladığımızı varsayalım:
    int tam = 33;
Bu değişken için, int tipinde bellekte (genellikle herbiri 1 bayt olan 4 bayt büyüklüğünde) bir hücre ayrılır ve o hücreye 33 sayısı ikilik (binary) sayı sitemindeki karşılığı olan 4 baytlık (32 bitlik):
00000000 00000000 00000000 00100001
sayısı elektronik olarak yazılır. tam değişkenine ait dört temel özellik Şekil 11.1'deki gibi gösterilebilir:


Şekil 11.1: Bir değişkene eşlik eden dört temel özellik
Bellek adresleri genellikle onaltılık (hexadecimal) sayı sisteminde ifade edilir. 0x3fffd14 sayısı onluk (decimal) sayı sisteminde 67108116 sayına karşık gelir. Bunun anlamı, tam değişkeni, program çalıştığı sürece, bellekte 67108116. - 67108120. numaralı gözler arasındaki 4 baytlık hücreyi işgal edecek olmasıdır. Şekil 11.1'deki gösterim, basit ama anlaşılır bir tasvirdir. Gerçekte, int tipindeki tam değişkeninin bellekteki yerleşimi ve içeriği (değeri) Şekil 11.2'de gösterildiği gibi olacaktır.


Şekil 11.2: tam adlı değişkenin bellekteki gerçek konumu ve ikilik düzendeki içeriği
Değişkenin saklı olduğu adres, & karakteri ile tanımlı adres operatörü ile öğrenilebilir. Bu operatör bir değişkenin önüne konursa, o değişkenin içeriği ile değil adresi ile ilgileniliyor anlamına gelir. Aşağıdaki program parçasının:
          int tam = 33;

          printf("icerik: %d\n",tam);
          printf("adres : %p\n",&tam);
çıktısı:
          icerik: 33
          adres : 3fffd14
şeklindedir. Burada birinci satır tam değişkeninin içeriği, ikinci ise adresidir. Adres yazdırılırken %p tip belirleyicisinin kullanıldığına dikkat ediniz.

Gösterici(Pointer) Nedir?
Gösterici, bellek alanındaki bir gözün adresinin saklandığı değişkendir. Göstericilere veriler (yani değişkenlerin içeriği) değil de, o verilerin bellekte saklı olduğu hücrenin başlangıç adresleri atanır. Kısaca gösterici adres tutan bir değişkendir.
Bir gösterici, diğer değişkenler gibi, sayısal bir değişkendir. Bu sebeple kullanılmadan önce program içinde bildirilmelidir. Gösterici tipindeki değişkenler şöyle tanımlanır:
          tip_adı *gösterici_adı;
Burada tip_adı herhangi bir C tip adı olabilir. Değişkenin önünedeki * karakteri yönlendirme (indirection) operatörü olarak adlandırılır ve bu değişkenin veri değil bir adres bilgisi tutacağını işaret eder. Örneğin:
          char  *kr;             /* tek bir karakter için */
          int   *x;              /* bir tamsayı için */
          float *deger, sonuc;   /* deger gösterici tipinde, sonuc sıradan bir gerçel değişkenler */
Yukarıda bildirilen göstericilerden; kr bir karakterin, x bir tamsayının ve deger bir gerçel sayının bellekte saklı olduğu yerlerin adreslerini tutar.
Bir göstericiye, bir değişkenin adresini atamak için adres operatörünü kullanabiliriz. Örneğin tamsayı tipindeki tam adlı bir değişken ve ptam bir gösterici olsun. Derleyicide, aşağıdaki gibi bir atama yapıldığında:
          int *ptam, tam = 33;
          .
          .
          .
          ptam = &tam;
ptam göstericisinin tam değişkeninin saklandığı adresi tutacaktır. Bu durum Şekil 11.3'deki gibi tasvir edilir.


Şekil 11.3: Göstericinin bir değişkenin adresini göstermesi
Şekil 11.3'deki gösterimde, ptam göstericisinin içeriği tam değişkeninin içeriği (33) değil adresidir (0x3fffd14). Ayrıca, ptam değişkeni, bellekte başka bir hücrede saklandığına ve bu hücrenin int değil int * tipinde bir bölge olduğuna dikkat ediniz. Buraya kadar anlatılanlar, Program 11.1'de özetlenmiştir.
Program 11.1: Bir değişkenin içeriğini ve adresini ekrana yazdırma
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
/* 10prg01.c: ilk gösterici programı */

#include <stdio.h>

int main()
{
   int *ptam, tam = 33;

   ptam = &tam;

   printf("tam:  icerik = %d\n", tam);
   printf("tam:   adres = %p\n",&tam);
   printf("tam:   adres = %p\n",ptam);

 return 0;
}
7. satırda değişkenler bildirilmiştir. 9. satırdaki atama ile tam değişkeninin adresiptam göstericisine atanmıştır. Bu satırdan itibaren ptamtam değişkeninin gösterir. 11. satıda tam'ın içeriği (33 sayısı), 12. ve 13. satırda tam'ın adresi, %p tip karakteri ile, ekrana yazdırılmıştır. Ekran çıktısı incelendiğinde, &tam ve ptam içereriğinin aynı anlamda olduğu görülür.
ÇIKTI

tam:  icerik = 33
tam:   adres = 0x3fffd14
tam:   adres = 0x3fffd14
tam adlı değişkenin içeriğine ptam gösterici üzerinde de erişilebilir. Bunun için program içinde ptam değişkeninin önüne yönelendirme operatörü (*) koymak yeterlidir. Yani *ptamtam değişkeninin adresini değil içeriğini tutar. Buna göre:
          *ptam = 44;
komutuyla, ptam'ın adresini tuttuğu hücreye 44 değeri atanır. Bu durum, Program 11.2'de gösterilmiştir.
Program 11.2: Bir değişkenin içeriğini ve adresini ekrana yazdırma
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
/* 10prg02.c: ikinci gösterici programı */

#include <stdio.h>

int main()
{
   int *ptam, tam = 33;

   ptam = &tam;    /* ptam -> tam */

   printf("&tam  = %p\n",&tam);
   printf("ptam  = %p\n",ptam);
   printf("\n");

   printf("tam   = %d\n",tam);
   printf("*ptam = %d\n",*ptam);
   printf("\n");

   *ptam = 44;     /* tam = 44 anlamında */

   printf("tam   = %d\n",tam);
   printf("*ptam = %d\n",*ptam);

 return 0;
}
ÇIKTI

&tam  = 0x3fffd14
ptam  = 0x3fffd14

tam   = 33
*ptam = 33

tam   = 44
*ptam = 44
Özetle ptam = &tam atamasıyla:
  • *ptam ve tamtam adlı değişkenin içeriği ile ilgilidir.
  • ptam ve &tamtam adlı değişkenin adresi ile ilgilidir.
  • * yönlendirme ve & adres operatörüdür.

11.3   Gösterici Aritmetiği
Göstericiler kullanılırken, bazen göstericinin gösterdiği adres taban alınıp, o adresten önceki veya sonraki adreslere erişilmesi istenebilir. Bu durum, göstericiler üzerinde, aritmetik işlemcilerin kullanılmasını gerektirir. Göstericiler üzerinde yalnızca toplama (+), çıkarma (-), bir arttırma (++) ve bir eksiltme (--) operatörleri işlemleri yapılabilir.
Aşağıdaki gibi üç tane gösterici bildirilmiş olsun:
          char   *kar;
          int    *tam;
          double *ger;
Bu göstericiler sırasıyla, bir karakter, bir tamsayı ve bir gerçel sayının bellekte saklanacağı adreslerini tutar. Herhangi bir anda, tuttukları adresler de sırasıyla 10000 (0x2710), 20000 (0x4e20) ve 30000 (0x7530) olsun. Buna göre aşağıdaki atama işelemlerinin sonucu:
          kar++;
          tam++;
          ger++;
sırasyla 10001 (0x2711), 20004 (0x4e24) ve 30008 (0x7538) olur. Bir göstericiye ekleme yapıldığında, o anda tuttuğu adres ile eklenen sayı doğrudan toplanmaz. Böyle olsaydı, bu atamaların sonuçları sırasıyla 10001, 20001 ve 30001 olurdu. Gerçekte, göstericiye bir eklemek, göstericinin gösterdiği yerdeki veriden hemen sonraki verinin adresini hesaplamaktır.
Genel olarak, bir göstericiye n sayısını eklemek (veya çıkarmak), bekllekte gösterdiği veriden sonra (veya önce) gelen n. elemanın adresini hesaplamaktır. Buna göre aşağıdaki atamalar şöyle yorumlanır.
          kar++;             /* kar = kar +   sizeof(char)   */
          tam = tam + 5;     /* tam = tam + 5*sizeof(int)    */
          ger = ger - 3;     /* ger = ger - 3*sizeof(double) */
Program 11.3, bu bölümde anlatlanları özetlemektedir. İnceleyiniz.
Program 11.3: Gösterici aritmetiği
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
/* 10prg03.c: gösterici aritmetiği */

#include <stdio.h>


int main()
{
   char   *pk, k = 'a';
   int    *pt, t = 22;
   double *pg, g = 5.5;

   pk = &k;
   pt = &t;
   pg = &g;

   printf("Onceki  adresler: pk= %p  pt= %p   pg= %p \n", pk, pt, pg);

   pk++;
   pt--;
   pg = pg + 10;

   printf("Sonraki adresler: pk= %p  pt= %p   pg= %p \n", pk, pt, pg);

 return 0;
}
ÇIKTI

Onceki  adresler: pk= 0xbfbbe88f  pt= 0xbfbbe888   pg= 0xbfbbe880
Sonraki adresler: pk= 0xbfbbe890  pt= 0xbfbbe884   pg= 0xbfbbe8d0

11.4   Gösterici ve Diziler Arasındaki İlişki
C dilinde göstericiler ve diziler arasında yakın bir ilişki vardır. Bir dizinin adı, dizinin ilk elemanının adresini saklayan bir göstericidir. Bu yüzden, bir dizinin herhangi bir elemanına gösterici ile de erişilebilir. Örneğin:
          int kutle[5], *p, *q;
şeklinde bir bildirim yapılsın. Buna göre aşağıda yapılan atamalar geçerlidir:
          p = &kutle[0];    /* birinci elemanın  adresi 
p göstericisine atandı */
          p = kutle;        /* birinci elemanın  adresi 
p göstericisine atandı */
          q = &kutle[4];    /* son      elemanın adresi 
q göstericisine atandı */
İlk iki satırdaki atamalar aynı anlamdadır. Dizi adı bir gösterici olduğu için, doğrudan aynı tipteki bir göstericiye atanabilir. Ayrıca, i bir tamsayı olmak üzere,
          kutle[i];
ile
          *(p+i);
aynı anlamdadır. Bunun sebebi, p göstericisi kutle dizisinin başlangıç adresini tutmuş olmasıdır. p+i işlemi ile i+1. elemanın adresi, ve *(p+i) ile de bu adresteki değer hesaplanır.


NOT : Bu veriyi Gaziantep Üniversite'sinin web-sitesinden anlatımı anlaşılır olduğu için aldım.

Pointer Kavramını bu koda bakarak pekiştirin!;

KOD : 

#include <stdio.h>
#include <stdlib.h>

int main()
{

    int a=5;
    int *p; //Pointer'lar yildiz ile birlikte tanitilir.

    p=&a;

    printf("A'nin degeri : %d\n", a);
    printf("A'nin Ram'deki yeri : %p\n", p);
    printf("A'nin Ram'deki yeri(Sayisal Olarak) : %d\n", p);
    printf("A'nin Ram'deki yerinde bulunan deger : %d\n", *p); //Bu deger zaten a'ya esittir.


    //kodlamazamani.blogspot.com.tr
    return 0;

}

EKRAN ÇIKTISI :






DİZİLER VE POİNTERLAR ARASINDAKİ İLİŞKİ İÇİN :
TIKLAYIN

Hiç yorum yok:

Yorum Gönder