3 Aralık 2009 Perşembe

Function Pointers in C

Fonksiyon göstergeleri (function pointers) bir fonksiyonun bellekte saklandığı adrese işaret eden göstergelerdir. Normal göstergeler gibi, bellekteki bir ögenin başlangıç adresini sakladıkları için, fonksiyon göstergeleri de bir değişkenle ifade edilir.

Peki bir fonksiyon göstergesini nasıl tanımlarız? Örneğin, int türünden bir parametre alan ve float türünden bir değer döndüren bir fonksiyona işaret edecek bir fonksiyon pointer tanımlamak isteyelim. Göstergeyle göstermek istediğimiz asıl fonksiyonumuz aşağıda verildiği gibi olsun.



float functionA(int value)
{
    return value * 1.5;
}

Bu fonksiyona işaret edecek fonksiyon göstergemizi tanımlarken fonksiyon prototipini aynen kopyalayıp yapıştıralım.


float functionA(int value);

Daha sonra fonksiyon ismini parantez içine alıp yeni bir isim verelim ve önüne * işareti koyalım.


float (*fPtr)(int value);

Böylece bu fonksiyona işaret edebilecek bir fonksiyon göstergesi tanımlamış olduk. Fonksiyon göstergelerini tanımlarken bu yolu kullanırsak hata yapma oranımız düşecektir.

Fonksiyon göstergesi kullanarak fonksiyon çağrısı yapma

Şimdi de bu fonksiyon göstergesine functionA fonksiyonun adresini atayarak fonksiyon adıyla değil de fonksiyon göstergesiyle nasıl fonksiyon çağrısı yapabileceğimizi(callback) gösterelim. Bunun için aşağıdaki küçük programa bir göz atalım.


#include   <stdio.h> 
float functionA(int value)
{
    return value * 1.5;
}

int main()
{
    float (*fPtr)(int value); 
    //fonksiyon göstergesi degiskenimizin adi : fPtr
    fPtr = &functionA;
    /*fonksiyon göstergesine functionA fonksiyonun adresini atadik
    fptr = functionA kullanimi da calisir.*/
    
    float result = fPtr(5);
    /*fonksiyon göstergesini kullanarak bir fonksiyon çagrisi yaptik(callback)
    (*fPtr)(5); gibi bir çagri da kullanilabilir. fPtr(5) cagrisi ile ozdestir.*/
    printf("%f\n",result);
    return 0;
}

Fonksiyon göstergelerini fonksiyonlara parametre olarak geçme

Fonksiyon göstergelerini fonksiyonlara parametre olarak da geçebiliriz. Örnek kullanımına aşağıda yer verdim.


#include <stdio.h> 

void calculate(float num1,float num2, float (*operate)(float,float))
{
    float result = operate(num1,num2);
    printf("Result: %f\n",result);
}

float add(float a,float b){ return a + b;}
float multiply(float a,float b){ return a * b;}
float divide(float a,float b){ return a / b;}
float subtract(float a,float b){ return a - b;}

int main()
{
    calculate(10,5,&add);
    //daha önce de belirttiğim gibi calculate(10,5,add); gibi adres operatörü olmadan da kullanılabilir.
    calculate(10,5,&multiply);
    calculate(10,5,&divide);
    calculate(10,5,&subtract);
    return 0;
}

Fonksiyonlarda dönüş değeri olarak fonksiyon göstergesi döndürme

Fonksiyonlarda dönüş değeri olarak bir fonksiyon göstergesi döndürmek de mümkündür. Bunun için gereken sözdizimi ilk başta biraz karmaşık gelebilir. O yüzden teorik olarak değil de örnekler üstünden anlatmayı tercih edeceğim.Örnek kod:


float (*GetOpearationFunctionPointer(const char opCode))(float, float)
{
   if(opCode == '+') return &add;
   else if(opCode == '-') return &subtract;
   else if(opCode == '*') return &multiply;
   else if(opCode == '/') return ÷
} 

Yukarıdaki örnek üzerinden konuşalım. Bu fonksiyonu yazma aşamasındaki adımlarımız;

* Fonksiyonumuzun adı GetOpearationFunctionPointer olsun
* const char türünden bir parametre alsın (opCode)
* parametre olarak geçilen opCode değerine göre ilgili fonksiyon göstergesi döndürülsün. Döndürülecek fonksiyon göstergesi, float (*fptr)(float,float); şeklinde tanımı yapılabilecek fptr değişkeni ile aynı türden olsun.

Ancak, typedef keywordünü kullanarak bu tanımı aşağıdaki gibi daha kolay bir şekilde yapmamız da mümkün.


typedef float (*fPtr)(float, float);
fPtr GetFunctionPointer(const char opCode);

Fonksiyon göstergeleri aygıt sürücü(device driver) programlamada,event-driven uygulamalarda ve kesilmelerle çalışırken de çok kullanılır. Bu konunun daha iyi anlaşılabilmesi için daha önce okulda kesilmelerle ilgili bir ödevimde kullandığım ve bir interrupt fonksiyon göstergesi döndüren fonksiyonun prototipini de ikinci bir örnek olarak vermek istiyorum.


void interrupt ( *getInterruptVector(int interruptId) )();

Bu prototipten fonksiyonun ne yaptığını yorumlamak istersek,

* Fonksiyonun adı getInterruptVector
* int türünde ve interruptId adında bir parametre alıyor, ve
* void interrupt (*interruptVector)(); şeklinde tanımı yapılabilecek interruptVector değişkeni ile aynı türden bir interrupt fonksiyon göstergesi döndürmektedir.


Fonksiyon göstergeleri kullanılarak pek çok yarar sağlayabiliriz.Örneğin;

* Seçenek sayısı arttıkça kabaran switch / if-else yapılarından kurtulabiliriz.

C diliyle, bu maddeyi açıklamak için yazdığım küçük bir programı kullanarak, bu maddeyi anlatmaya çalışacağım. Bu program komut satırında çalışan, kullanıcıya bir menü sunan ve klavyeden bir giriş yapılmasını bekleyen, yapılan bu seçime göre ilgili işlemi gerçekleştiren en temel anlamda bir menü uygulamasıdır.


#include <stdio.h> 

typedef void (*menuItemFunction)(void);

void printMenu();
void initialize(menuItemFunction* functions);
void execute(menuItemFunction* functions);

void function1();
void function2();
void function3();
void function4();
void function5();
void function6();

int main()
{
    menuItemFunction menuFunctions[6];
    initialize(menuFunctions);
    execute(menuFunctions);
    return 0;
}

void execute(menuItemFunction* functions)
{
    char choice;
    while(1)
    {
        printMenu();
        scanf("%c",&choice);
        
        /*burada kabarabilecek switch / if-else kod parcasindan asagidaki gibi kurtuluyoruz*/
        if(choice > '0' && choice < '7')
            functions[choice - '0' - 1]();
            /*'0' cikararak 0 karakterinin sayisal karsiligini elde ediyoruz
            menüde secenekler 0'dan degil de 1 den basladigi icin bir de 1 cikardik
            (yukarida kisaca index degerini elde ettik)*/
        else if(choice == '0') break;
        else printf("Invalid enterence!");
        
        fflush(stdin);
    }
}

void printMenu()
{
    printf("\n\n\n\n\n");
    printf("\t\t\t1. Menu Item 1\n");
    printf("\t\t\t2. Menu Item 2\n");
    printf("\t\t\t3. Menu Item 3\n");
    printf("\t\t\t4. Menu Item 4\n");
    printf("\t\t\t5. Menu Item 5\n");
    printf("\t\t\t6. Menu Item 6\n");
    
    printf("\n\t\t\tYour choice -> ");
    
}

void initialize(menuItemFunction* functions)
{
    functions[0] =  function1;
    functions[1] = &function2;
    functions[2] = &function3;
    functions[3] = &function4;
    functions[4] = &function5;
    functions[5] = &function6;
    
}

void function1(){ printf("Menu Item 1 selected\n");}
void function2(){ printf("Menu Item 2 selected\n");}
void function3(){ printf("Menu Item 3 selected\n");}
void function4(){ printf("Menu Item 4 selected\n");}
void function5(){ printf("Menu Item 5 selected\n");}
void function6(){ printf("Menu Item 6 selected\n");}


* Fonksiyon göstergeleri ayrıca, nesneye yönelik programlama dillerinde daha haşır neşir olduğumuz "late-binding" kavramını geliştirmeye de olanak tanır.

Fonksiyon göstergelerini kullanımak diğer göstergelerle karşılaştırıldığında çok daha az risklidir. Çünkü herhangi bir şekilde bellekte yer alma veya verme işlemleri gerektirmez.


Kaynaklar : http://www.newty.de/fpt/index.html

5 yorum:

  1. Eyw. eline sağlık seyfullah hocam...

    YanıtlaSil
  2. Seyfullah, yazdığın kodları pre ve code etiketleri içine almıyorsun sanırım. Bloga kod eklerken html etiketleri çok işe yarıyor, kodlarını daha görünür ve okunur kılacaktır. İyi bir yazı, eline sağlık.

    YanıtlaSil
  3. Okuduğun ve beğendiğin için teşekkür ederim Seval. Eleştirin için de ayrıca teşekkürler.. Önerini de dikkate alıp yazımı güncelledim, bu şekilde gerçekten de daha güzel oldu..

    YanıtlaSil
  4. Bende bu konuda bir yazı yazmayı düşünüyordum. Eline sağlık. Auto Syntax Highlighter yada Google Syntax Highlighter öneriyorum kod renklendirme için :)

    YanıtlaSil
  5. Guzel bir yazı olmus. Bilmedigim bir konu oldugu için giriş seviyesinde öğrenmiş oldum. Elinize ve emeğinize sağlık..

    YanıtlaSil

 
Web Analytics Page copy protected against web site content infringement by Copyscape Software Blogs - BlogCatalog Blog Directory