C Programlama Dili'ne Giriş

  Dersler
* �ns�z

* Giriş
* Veri Tipleri, De�i�kenler
* Operat�rler
* Temel G/� Fonksiyonlar�
* Temel K�t�phane Fonksiyonlar�
* Kar��la�t�rma Deyimleri
* D�ng�ler
* Fonksiyonlar I
* Fonksiyonlar II
* Diziler
* G�sterici (Pointer) Kavram�
* Katarlar (Stringler)
* Dinamik Bellek Y�netimi
* G�sterici Uygulamalar�
* Yap�lar ve Birlikler
* Dosya Y�netimi
* Bit D�zeyinde �al��mak
* Port Denetimi
* Grafik Kullan�m�
* C Makrolar�

* K�saca C++
* Derleme Se�enekleri
* Tarih-Saat Fonksiyonlar�
* Monte-Carlo Y�ntemleri
* Fortran ve C

* Yararlan�lan Kaynaklar

  C/C++ Derleyicileri
* Dev-C++
* Salford (silversoft FTN95)
* GCC
* Turbo C
* Eclipse IDE
* NetBeans IDE

  D�� Ba�lant�lar
* programlama.com
* C Programc�lar� Derne�i

* C (wikipedia)
* C++ (wikipedia)
* cplusplus.com
* koders.com
* Hot scripts

 

Ders 11: Gsterici (Pointer) Kavrami

###################- (%99)

En son gncelleme: Wed, 30 Nov 2011 13:22:02 +0200

Giris

Hemen hemen btn programlama dillerinin temelinde gsterici (pointer) veri tipi bulunmaktadir. Bir ok dil gsterici kullanimini kullaniciya sunmamistir veya ok sinirli olarak sunmustur. Fakat C Progrmalama Dili'nde gstericiler yogun olarak kullanilir. Hatta gsterici kavrami C dilinin bel kemigidir. Kavranmasi biraz g olan gstericiler iin -latife yapilip- C kullanicilarini "gsterici kullanabilenler ve kullanmayanlar" olmak zere iki gruba ayiranlar da olmustur. zetle, bir C programcisi gsterici kavramini anlamadan C diline hakim olamaz.

Trke yazilan C kitaplarda pointer kelimesi yerine asagidaki ifadelerden biri karsilasilabilir:

pointer = isareti = gsterici = gsterge

Anlatimda, gsterici terimini kullanacagiz.


11.1   Degisken ve Bellek Adresi

Bilgisayarin ana bellegi (RAM) sirali kaydetme gzlerinden olusmustur. Her gze bir adres atanmistir. Bu adreslerin degerleri 0 ila bellegin sahip oldugu st degere bagli olarak degisebilir. rnegin 1GB MB bir bellek, 1024*1024*1024 = 1073741824 adet gzden olusur. Degisken tiplerinin bellekte isgal ettigi alanin bayt cinsinden uzunlugu sizeof() operatryle grenildigini hatirlayin. (bkz: Program 2.1).

Bir programlama dillinde, belli bir tipte degisken tanimlanip ve bir deger atandiginda, o degiskene drt temel zellik eslik eder:

  1. degiskenin adi
  2. degiskenin tipi
  3. degiskenin sahip oldugu deger (ierik)
  4. degiskenin bellekteki adresi

rnegin tam adli bir tamsayi degiskenini asagidaki gibi tanimladigimizi varsayalim:

    int tam = 33;

Bu degisken iin, int tipinde bellekte (genellikle herbiri 1 bayt olan 4 bayt byklgnde) bir hcre ayrilir ve o hcreye 33 sayisi ikilik (binary) sayi sitemindeki karsiligi olan 4 baytlik (32 bitlik):

00000000 00000000 00000000 00100001

sayisi elektronik olarak yazilir. tam degiskenine ait drt temel zellik ekil 11.1'deki gibi gsterilebilir:



ekil 11.1: Bir degiskene eslik eden drt temel zellik

Bellek adresleri genellikle onaltilik (hexadecimal) sayi sisteminde ifade edilir. 0x3fffd14 sayisi onluk (decimal) sayi sisteminde 67108116 sayina karsik gelir. Bunun anlami, tam degiskeni, program alistigi srece, bellekte 67108116. - 67108120. numarali gzler arasindaki 4 baytlik hcreyi isgal edecek olmasidir. ekil 11.1'deki gsterim, basit ama anlasilir bir tasvirdir. Gerekte, int tipindeki tam degiskeninin bellekteki yerlesimi ve ierigi (degeri) ekil 11.2'de gsterildigi gibi olacaktir.



ekil 11.2: tam adli degiskenin bellekteki gerek konumu ve ikilik dzendeki ierigi

Degiskenin sakli oldugu adres, & karakteri ile tanimli adres operatr ile grenilebilir. Bu operatr bir degiskenin nne konursa, o degiskenin ierigi ile degil adresi ile ilgileniliyor anlamina gelir. Asagidaki program parasinin:

          int tam = 33;

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

iktisi:

          icerik: 33
          adres : 3fffd14

seklindedir. Burada birinci satir tam degiskeninin ierigi, ikinci ise adresidir. Adres yazdirilirken %p tip belirleyicisinin kullanildigina dikkat ediniz.


11.2   Gsterici Nedir?

Gsterici, bellek alanindaki bir gzn adresinin saklandigi degiskendir. Gstericilere veriler (yani degiskenlerin ierigi) degil de, o verilerin bellekte sakli oldugu hcrenin baslangi adresleri atanir. Kisaca gsterici adres tutan bir degiskendir.

Bir gsterici, diger degiskenler gibi, sayisal bir degiskendir. Bu sebeple kullanilmadan nce program iinde bildirilmelidir. Gsterici tipindeki degiskenler syle tanimlanir:

          tip_adi *gsterici_adi;

Burada tip_adi herhangi bir C tip adi olabilir. Degiskenin nnedeki * karakteri ynlendirme (indirection) operatr olarak adlandirilir ve bu degiskenin veri degil bir adres bilgisi tutacagini isaret eder. rnegin:

          char  *kr;             /* tek bir karakter iin */
          int   *x;              /* bir tamsayi iin */
          float *deger, sonuc;   /* deger gsterici tipinde, sonuc siradan bir gerel degiskenler */

Yukarida bildirilen gstericilerden; kr bir karakterin, x bir tamsayinin ve deger bir gerel sayinin bellekte sakli oldugu yerlerin adreslerini tutar.

Bir gstericiye, bir degiskenin adresini atamak iin adres operatrn kullanabiliriz. rnegin tamsayi tipindeki tam adli bir degisken ve ptam bir gsterici olsun. Derleyicide, asagidaki gibi bir atama yapildiginda:

          int *ptam, tam = 33;
          .
          .
          .
          ptam = &tam;

ptam gstericisinin tam degiskeninin saklandigi adresi tutacaktir. Bu durum ekil 11.3'deki gibi tasvir edilir.



ekil 11.3: Gstericinin bir degiskenin adresini gstermesi

ekil 11.3'deki gsterimde, ptam gstericisinin ierigi tam degiskeninin ierigi (33) degil adresidir (0x3fffd14). Ayrica, ptam degiskeni, bellekte baska bir hcrede saklandigina ve bu hcrenin int degil int * tipinde bir blge olduguna dikkat ediniz. Buraya kadar anlatilanlar, Program 11.1'de zetlenmistir.

Program 11.1: Bir degiskenin ierigini ve adresini ekrana yazdirma
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
/* 10prg01.c: ilk gsterici programi */

#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. satirda degiskenler bildirilmistir. 9. satirdaki atama ile tam degiskeninin adresi, ptam gstericisine atanmistir. Bu satirdan itibaren ptam, tam degiskeninin gsterir. 11. satida tam'in ierigi (33 sayisi), 12. ve 13. satirda tam'in adresi, %p tip karakteri ile, ekrana yazdirilmistir. Ekran iktisi incelendiginde, &tam ve ptam iereriginin ayni anlamda oldugu grlr.

IKTI

tam:  icerik = 33
tam:   adres = 0x3fffd14
tam:   adres = 0x3fffd14

tam adli degiskenin ierigine ptam gsterici zerinde de erisilebilir. Bunun iin program iinde ptam degiskeninin nne ynelendirme operatr (*) koymak yeterlidir. Yani *ptam, tam degiskeninin adresini degil ierigini tutar. Buna gre:

          *ptam = 44;

komutuyla, ptam'in adresini tuttugu hcreye 44 degeri atanir. Bu durum, Program 11.2'de gsterilmistir.

Program 11.2: Bir degiskenin ierigini ve adresini ekrana yazdirma
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 gsterici programi */

#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 anlaminda */

   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 atamasiyla:
  • *ptam ve tam, tam adli degiskenin ierigi ile ilgilidir.
  • ptam ve &tam, tam adli degiskenin adresi ile ilgilidir.
  • * ynlendirme ve & adres operatrdr.

11.3   Gsterici Aritmetigi

Gstericiler kullanilirken, bazen gstericinin gsterdigi adres taban alinip, o adresten nceki veya sonraki adreslere erisilmesi istenebilir. Bu durum, gstericiler zerinde, aritmetik islemcilerin kullanilmasini gerektirir. Gstericiler zerinde yalnizca toplama (+), ikarma (-), bir arttirma (++) ve bir eksiltme (--) operatrleri islemleri yapilabilir.

Asagidaki gibi tane gsterici bildirilmis olsun:
          char   *kar;
          int    *tam;
          double *ger;

Bu gstericiler sirasiyla, bir karakter, bir tamsayi ve bir gerel sayinin bellekte saklanacagi adreslerini tutar. Herhangi bir anda, tuttuklari adresler de sirasiyla 10000 (0x2710), 20000 (0x4e20) ve 30000 (0x7530) olsun. Buna gre asagidaki atama iselemlerinin sonucu:

          kar++;
          tam++;
          ger++;

sirasyla 10001 (0x2711), 20004 (0x4e24) ve 30008 (0x7538) olur. Bir gstericiye ekleme yapildiginda, o anda tuttugu adres ile eklenen sayi dogrudan toplanmaz. Byle olsaydi, bu atamalarin sonulari sirasiyla 10001, 20001 ve 30001 olurdu. Gerekte, gstericiye bir eklemek, gstericinin gsterdigi yerdeki veriden hemen sonraki verinin adresini hesaplamaktir.

Genel olarak, bir gstericiye n sayisini eklemek (veya ikarmak), bekllekte gsterdigi veriden sonra (veya nce) gelen n. elemanin adresini hesaplamaktir. Buna gre asagidaki atamalar syle yorumlanir.

          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 blmde anlatlanlari zetlemektedir. Inceleyiniz.

Program 11.3: Gsterici aritmetigi
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: gsterici aritmetigi */

#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   Gsterici ve Diziler Arasindaki Iliski

C dilinde gstericiler ve diziler arasinda yakin bir iliski vardir. Bir dizinin adi, dizinin ilk elemaninin adresini saklayan bir gstericidir. Bu yzden, bir dizinin herhangi bir elemanina gsterici ile de erisilebilir. rnegin:

          int kutle[5], *p, *q;

seklinde bir bildirim yapilsin. Buna gre asagida yapilan atamalar geerlidir:

          p = &kutle[0];    /* birinci elemanin  adresi p gstericisne atandi */
          p = kutle;        /* birinci elemanin  adresi p gstericisne atandi */
          q = &kutle[4];    /* son      elemanin adresi q gstericisne atandi */

Ilk iki satirdaki atamalar ayni anlamdadir. Dizi adi bir gsterici oldugu iin, dogrudan ayni tipteki bir gstericiye atanabilir. Ayrica, i bir tamsayi olmak zere,

          kutle[i];
ile
          *(p+i);

ayni anlamdadir. Bunun sebebi, p gstericisi kutle dizisinin baslangi adresini tutmus olmasidir. p+i islemi ile i+1. elemanin adresi, ve *(p+i) ile de bu adresteki deger hesaplanir.

 NOT
Bir dizinin, i. elemanina erismek iin *(p+i) islemi yapilmasi zorunludur. Yani
  *p+i;   /* p nin gsterdigi degere (dizinin ilk elemanina) i sayisini ekle */
  *(p+i); /* p nin gsterdigi adresten i blok tedeki sayiyi hesapla */
anlamindadir. nk, * operatr + operatrne gre islem nceligine sahiptir.

Program 11.4'de tanimlanan fonksiyon kendine parameter olarak gelen n elemanli bir dizinin aritmetik ortlamasini hesaplar.

Program 11.4: Bir dizi ile gsterici arasindaki iliski
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: 
26: 
27: 
28: 
29: 
30: 
31: 
/* 10prg04.c: gsterici dizi iliskisi */

#include <stdio.h>

double ortalama(double dizi[], int n);

int main()
{

   double a[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
   double o;

   o = ortalama(a,5);

   printf("Dizinin ortalamasi = %lf\n",o);

 return 0;
}

double ortalama(double dizi[], int n)
{
   double *p, t=0.0;
   int i;
  
   p = dizi;    /* veya p = &dizi[0] */

   for(i=0; i<n; i++)
      t += *(p+i);

   return (t/n);
}

IKTI

Dizinin ortalamasi = 3.300000

20. - 31. satirda tanimlanan fonksiyon asagidaki gibi de yazilabilirdi:

          double ortalama(double dizi[], int n)
          {
             double *p, t=0.0;
   
             for(p=dizi; p < &dizi[n]; p++)
                t += *p;
  
             return (t/n);
          }

Bu fonksiyonda, dng sayaci iin (i degiskeni) kullanilmayip, dng iinde dizinin baslangi adresi p gstericisine atanmis ve kosul kisminda adres karsilastirilmasi yapilmistir. Bu durumda dng, p'nin tuttugu adresten baslar, ve p'nin adresi dizinin son elemaninin adresinden (&dizi[n-1]) kk veya esit oldugu srece evrim yinelenir.


11.5   Fonksiyon Parametresi Olan Gstericiler

C (ve C++) programlama dilinde fonksiyon parametreleri deger geerek (pass by value) yada adres geerek (pass by reference) olarak geilebilir. Blm 8'deki uygulamalarda fonksiyonlara parametreler deger geerek tasinmisti. Bu sekilde geirilen parametreler, fonksiyon iersinde degistirilse bile, fonksiyon agilildiktan sonra bu degisim agrilan yerdeki degerini degistirmez. Fakat, bir parametre adres geerek aktarilisa, fonksiyon iindeki degisikler geilen parametreyi etkiler. Adres geerek aktarim, gsterici kullanmayi zorunlu kilar.

rnegin, Program 11.5'de fonksiyonlara deger ve adres geerek aktarimin nasil yapilacagi gsterilmistir.

Program 11.5: Bir degiskenin ierigini ve adresini ekrana yazdirma
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: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
/* 10prg05.c: Deger geerek ve adres geerek aktarim */

#include <stdio.h>

void f1(int  );   /* iki fonksiyon */
void f2(int *);

int main()
{
   int x = 55;

   printf("x in degeri,\n");
   printf("Fonksiyonlar cagrilmadan once: %d\n",x);

   /* f1 fonksiyonu agriliyor...*/ 
   f1(x);     
   printf("f1 cagirildiktan sonra       : %d\n",x);


   /* f2 fonksiyonu agriliyor...*/ 
   f2(&x);
   printf("f2 cagirildiktan sonra       : %d\n",x);

 return 0;
}


/* Deger geerek aktarim */
   void f1(int n){
      n = 66;
      printf("f1 fonksiyonu icinde         : %d\n",n);
   }

/* Adres geerek aktarim */
   void f2(int *n){
      *n = 77;
      printf("f2 fonksiyonu icinde         : %d\n",*n);
   }

5. ve 6. satirlada kendine geilen parametrenin degerini alan f1 fonksiyonu ve parametrenin adresini alan f2 adli iki fonksiyon rnegi belirtilmisdir. 11. satirdaki x degiskeni 16. ve 21. satirlarda, f1(x) ve f2(&x) fonksiyonlarina, sirasiyla deger ve adres geerek aktarilmistir. f1 iinde x (n = 66; islemi ile) degisime ugramis, fakat agrilma isleminin sonucunda, x'in degeri degismemistir. Ancak f2 iinde x'in ( *n = 77 islemi ile) degisimi, agrildiktan sonrada korunmustur. Yani, adres geerek yaplian aktarimda, f2'ye aktarilan deger degil adres oldugu iin, yollanan x parametresi f2 iinde degisiklige ugrayacak ve bu degisim agrildigi 21. satirdan itibaren devam edecektir.

IKTI

x in degeri,
Fonksiyonlar cagrilmadan once: 55
f1 fonksiyonu icinde         : 66
f1 cagirildiktan sonra       : 55
f2 fonksiyonu icinde         : 77
f2 cagirildiktan sonra       : 77

Program 11.6'da iki tamsayi degiskeninin nasil takas (swap) edilecegi gsterilmistir. Bu islemi C porgramlama dilinde, eger degiskenler global olarak bildirilmemisse, gsterici kullanmadan bu islemi yapmak imkansizdir.

Program 11.6: Iki tamsayinin birbiri ile takas edilmesi
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: 
26: 
27: 
28: 
29: 
30: 
31: 
/* 10prg06.c: iki sayinin birbiri ile takas edilmesi */

#include <stdio.h>

void takas(int *, int *);

int main()
{
   int a, b;

   a=22; b=33;

   printf("takas oncesi : a=%d   b=%d\n",a,b);

   takas(&a, &b);

   printf("takas sonrasi: a=%d   b=%d\n",a,b);


 return 0;
}


void takas(int *x, int *y)
{
  int z;

   z = *x;
  *x = *y;
  *y =  z;
}

IKTI

takas oncesi : a=22   b=33
takas sonrasi: a=33   b=22


11.6   Geri Dns Degeri Gsterici Olan Fonksiyonlar

Fonkiyonlarin geri dns degeri bir gsterici olabilir. Bu durumda fonksiyon bir deger degil adres dndrecek demektir.

Program 11.7'da nce bir dizinin indisleri, dizi degerleri ve dizi elemanlarinin adresleri ekrana basilir. Daha sonra, maxAdr(); fonksiyonu ile dizinin en byk elemaninin adresi dndrlr. Bu rnek progam, gstericilerin gcn ok zarif bir biimde bize sunmaktadir. Ltfen inceleyiniz.

Program 11.7: Bir dizinin en byk elemaninin adresini grenmek
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: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
/* 10prg07.c:  geri donus degeri gosterici olan fonksiyon */

#include <stdio.h>

double* maxAdr(double a[], int boyut){
  double  ebd =  a[0];
  double *eba = &a[0];
  int i;
  for(i=1; i<boyut; i++){
    if(a[i]>ebd){
      ebd =  a[i]; // en byk deger
      eba = &a[i]; // en byk adres
    }
  }
  return eba;
}


int main()
{
  double x[6] = {1.1, 3.3, 7.1, 5.4, 0.2, -1.5};
  double *p;
  int k;
  // indis, dizi ve adresini ekrana bas
  for(k=0; k<6; k++){
    printf("%d %lf %p\n", k, x[k], &x[k]);
  }

  p = maxAdr(x,6);

  printf("En byk deger: %lf\n", *p);
  printf("En byk adres: %p \n",  p);
  printf("En byk konum: %d \n",  int(p-&x[0]));

  return 0;
}

Dizi elemanlari 21. satirda belirlenir. Bu dizinin indisleri, degerleri ve adresleri 26. satirda ekrana basilmistir. En byk elemanin adresi 29. satirdaki p = maxAdr(a,6); ile p gstericisine atanmistir. 5. satirda bildirilen maxAdr(); fonksiyonu, en byk elemanin adresini hesaplayip agrilan yere gnderir. Burada dikkat edilmesi gereken husus, fonksiyonun dns degerinin yerel eba gstericisi olmasidir. eba gstericisi 12. satirda hesaplanan ve fonksiyon parametersi olan dizinin en byk elemanin adresini tutmaktadir. Son olarak, fonksiyon agirildiktan sonra, p gstericisin gsterdigi deger, tuttugu adres ve dizinin birinci elemanina gre konumu (indisi) ekrana basilmistir. Indis hesabi int(p-&x[0]) islemi ile yapilabilir. Bu aslinda, p gstericisin tuttugu adres ile dizinin ilk elemaninin adresi arasindaki farktir. Sonu yine bir adres oldugu iin tamsayi deger elde etmek iin int() takisi kullanilmistir. Netice itibariyla bir fonksiyon ile sey ayni anda grenilmis olur.

IKTI

0 1.100000 0x7fff41b29ec0
1 3.300000 0x7fff41b29ec8
2 7.100000 0x7fff41b29ed0
3 5.400000 0x7fff41b29ed8
4 0.200000 0x7fff41b29ee0
5 -1.500000 0x7fff41b29ee8

En byk deger: 7.100000
En byk adres: 0x7fff41b29ed0
En byk konum: 2


11.7   Fonksiyon Gstericileri

Fonksiyon gstericileri, gsterici (pointer) kavraminin gcn gsterin diger bir uygulama alanidir. Dizilerde oldugu gibi, fonksiyon adlari da sabit gstericidir.
Fonksiyon betiginin (kodlarinin) bellekte bir adreste tutuldugu seklinde dsnebiliriz. Fonksiyon gstericisi basit olarak fonksiyon adinin saklandigi bellek adresini tutan bir gstericidir. Fonksiyon gstericileri sayesinde fonksiyonlar baska fonksiyonlara parametre olarak aktarilabilmektedir.

Fonksiyon adinin bellete yer isgal ettigi syle grenilebilir:

          int f(int);      /* fonksiyon bildirimi */ 
          int (*pf)(int);  /* fonksiyon gstericisi bildirimi */ 
          pf = &f;         /* f'nin adresini pf'ye ata! */

Program 11.8: Bir fonksiyonun 'adresini' iki yoldan grenme
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
/* 10prg08.c:  Bir fonksiyonun 'adresini' grenme */

#include <stdio.h>

int f(int n){
  int f=1, i;
  for(i=1; i<n; i++)
    f*=i;
  return f;
}

int main()
{
  int (*pf)(int);
  pf = &f;

  printf("Fonksiyonun adresi = %p\n", &f);
  printf("Fonksiyonun adresi = %p\n", pf);

  return 0;
}

IKTI

Fonksiyonun adresi = 0x4005b0
Fonksiyonun adresi = 0x4005b0

Asagidaki ikinci rnekte, bir fonksiyon diger fonksiyona parametre olarak geirilmis ve sayisal trevi hesaplanmistir. Trev hesaplanirken merkezi fark yaklasimi (central difference approximation) yntemi kullanilmistir.

 NOT
mfy ynteminde f(x) fonksiyonunun (h kk bir deger olmak zere) Taylor ailimlari syledir:

      f(x+h) = f(x) + h*f'(x) + h2*f''(x)/2! + h3*f'''(x)/3!  + ...
      f(x-h) = f(x) - h*f'(x) + h2*f''(x)/2! - h2*f'''(x)/3!  + ...
   -
   -----------------------------------------------------------------------
      f(x+h) - f(x-h) = 2*h*f'(x) + O(h3)
Burada O(h3)'l terimler ihmal edilirse birinci trev yaklasik olarak:

  f'(x) = [f(x+h) - f(x-h)]/2h
forml ile hesaplanir.

Program 11.9: Trev alan fonksiyon
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: 
26: 
27: 
28: 
/* 10prg09.c:  Fonksiyon Gstericisi ile trev hesabi */

#include <stdio.h>

double f(double);
double turev( double (*)(double), double);

int main()
{

  double x = 1.1;

  printf("Fonksiyon x = %lf deki degeri = %lf\n", x, f(x));
  printf("Fonksiyon x = %lf deki turevi = %lf\n", x, turev(f, x) );

  return 0;
}


// trevi hesaplanacak fonksiyon
double f(double x){
  return x*x*x - 2*x + 5.;
}
// sayisal trev alan fonksiyon
double turev( double (*fonk)(double x), double x){
  double h = 1.0e-3;
  return (fonk(x+h)-fonk(x-h)) / (2*h);
}

IKTI

Fonksiyon x = 1.100000 deki degeri = 4.131000
Fonksiyon x = 1.100000 deki turevi = 1.630001


11.8   NULL Gsterici

Bir gstericinin bellekte herhangi bir adresi gstermesi, veya ncden gstermis oldugu adres iptal edilmesi istemirse NULL sabiti kullanilir. Bu sabit derleyicide ASCII karakter tablosunun ilk karakteridir ve '\0' ile sembolize edilir.

          int *ptr, a = 12;
          .
          .
          ptr = &a;     /* ptr bellekte a degiskenin saklandigi yeri gsteriyor  */
          .
          .
          ptr = NULL;   /* ptr bellekte hi bir hcreyi gstermiyor */
          *ptr = 8      /* hata! NULL gstericinin gsterdigi yere bir deger atanamaz */

11.9   void Tipindeki Gstericiler

void gstericiler herhangi bir veri tipine ait olmayan gstericilerdir. Bu zelliginden dolayi, void gsterici genel gsterici (generic pointer) olarak da adlandirilir.

void gstericiler, void anahtar szcg ile bildirilir. rnegin:

          void *adr;
gibi.

void gstericiler yalnizca adres saklamak iin kullanilir. Bu yzden diger gstericiler arasinda atama islemlerinde kullanilabilir. rnegin asagidaki atamada derleyici bir uyari veya hata mesaji vermez:

          void *v;
          char *c;
          .
          .
          .
          v = c;   /* sorun yok !*/

Program 11.10'de void tipindeki bir gstericinin, program iinde, farkli tipteki verileri nasil gsterecegi ve kullanilacagi rneklenmistir. Inceleyiniz.

Program 11.10: void gsterici ile farkli tipteki verileri gsterme
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
/* 10prg10.c:  void gosterici (generic pointer) uygulamasi */

#include <stdio.h>

int main()
{
  char    kar = 'a';
  int     tam = 66;
  double  ger = 1.2;
  void   *veri;

  veri = &kar;
  printf("veri -> kar: veri  %c  karakter degerini gosteriyor\n", *(char *) veri);

  veri = &tam;
  printf("veri -> tam: simdi veri  %d  tamsayi degerini gosteriyor\n", *(int *) veri);

  veri = &ger;
  printf("veri -> ger: simdi de veri  %lf  gercel sayi degerini gosteriyor\n", *(double *) veri);

  return 0;
}

IKTI

veri -> kar: veri  a  karakter degerini gosteriyor
veri -> tam: simdi veri  66  tamsayi degerini gosteriyor
veri -> ger: simdi de veri  1.200000  gercel sayi degerini gosteriyor

Benzer olarak, fonksiyon parameterelerinin kopyalanmasi sirasinda da bu trden atama islemleri kullanilabilir. Uygulamada, tipten bagimsiz adres islemlerinin yapildigi fonksiyonlarda, parametre degiskeni olarak void gstericiler kullanilir. rnegin

          void free (void *p)
          {
             .
             .
             .
          }

Parametresi void *p olan free fonksiyonu, herhangi trden gsterici ile agrilabilir.



Powered by PHP