اشاره‌گر به تابع

یک اشاره‌گر به تابع (یا اشاره‌گر به روال)، نوعی اشاره‌گر است که در زبان‌های برنامه‌نویسی نسل سوم (همانند فرترن، کوبول و سی) و همین‌طور زبان‌های شی گرا (همانند سی++ و دی) پشتیبانی می‌شود. این نوع اشاره‌گر، به جای اشاره کردن به مقادیر داده‌ای، به قطعه کدی قابل اجرا در حافظه اشاره می‌کند. از طریق یک اشاره‌گر به تابع، می‌توان تابع مورد اشاره را به صورت غیرمستقیم فراخوانی کرده و آرگومان‌های تابع را هم دقیقاً مانند فراخوانی مستقیم (بدون استفاده از اشاره‌گر)، به تابع ارسال کرد. به عمل فراخوانی توابع به کمک یک اشاره‌گر، معمولاً تحت عنوان «فراخوانی غیر مستقیم» یاد می‌شود، چرا که به جای فراخوانی مستقیم تابع به وسیلهٔ یک نام ثابت یا یک آدرس، تابع با استفاده از یک متغیر و به صورت غیرمستقیم فراخوانی می‌شود. به کمک اشاره‌گر به تابع، می‌توان بر اساس مقادیر زمان اجرا، تابعی خاص را برای اجرا شدن انتخاب کرد و بدین ترتیب کد را ساده‌تر کرد.

ساده‌ترین پیاده‌سازی اشاره‌گر به تابع، متغیری است که حاوی آدرس یک تابع است و این تابع در داخل حافظه قرار دارد. زبان‌های قدیمی نسل سوم همانند کوبول و همچنین زبان‌های مدرنی مانند زبان سی، اشاره‌گر به تابع را به این صورت پیاده‌سازی کرده‌اند. در زبان‌های قدیمی‌تر، اشاره‌گر به توابع معمولاً نسبت به زبان‌های مدرن‌تر از ایمنی نوع کمتری برخوردارند. در زبان‌های مدرن، معمولاً در هنگام تعریف کردن یک اشاره‌گر به تابع، اطلاعات بیشتری راجع به نوع‌داده‌ها مشخص می‌شود، از جمله نوع مقدار برگشتی تابع، تعداد و نوع پارامترهای تابع و ... .

مثال در زبان سی 
در زبان برنامه‌نویسی سی، اشاره‌گر به توابع، خیلی متداول نیستند. یک اشاره‌گر به تابع به شکل زیر اعلان می‌شود:

ret-type (*name) (arg1-type, arg2-type, ..., argn-type)
double (*fp) (double)

خط اول حالت کلی را نشان می‌دهد و خط دوم هم یک مثال است. در دستور دوم، اشاره‌گری به نام fp را اعلان می‌کند. این اشاره‌گر می‌تواند به تابعی اشاره کند که پارامتری از نوع double دارد و در برگشت هم مقداری از نوع double را برمی‌گرداند. برای اینکه اشاره‌گر بالا به تابعی اشاره کند، باید آدرس تابع مورد نظر در داخل اشاره‌گر قرار گیرد. در زبان سی، نام یک تابع، آدرس ابتدای محلی از حافظه است که کدهای تابع در آنجا قرار دارند؛ بنابراین برای مقدار دهی به یک اشاره‌گر به یک تابع (مثلاً sin()) می‌توان به صورت زیر عمل کرد.

fp = sin;

استفاده از اشاره‌گر به تابع، می‌تواند باعث شود کد کمی غیرقابل فهم شود، برای سادگی بیشتر، استفاده از typedefها برای اعلان اشاره‌گر به توابع بسیار رایج است.

typedef float (*funcp) (int)

funcp p1, p2;

در مثال بالا، p1 و p2، اشاره گر به تابعی هستند که پارامتری از نوع int دارد و مقداری از نوع float را برمی‌گرداند. می‌توان یک اشاره‌گر به تابع را به عنوان آرگومانی برای یک تابع دیگر ارسال کرد، آرایه‌ای از اشارگر به توابع داشت و ...

برنامه زیر، در داخل تابعی به نام compute_sum، از یک اشاره‌گر برای فراخوانی یکی از دو تابع sin یا cos استفاده می‌کند. اولین پارامتر تابع compute_sum یک اشاره‌گر به تابع است. برنامه زیر در داخل تابع main()، دو بار تابع compute_sum را فراخوانی می‌کند و در بار اول اشاره‌گری به sin() و در بار دوم اشاره‌گری به cos() برای compute_sum ارسال می‌کند. تابع compute_sum هم یکی از این دو تابع را به روشی غیرمستقیم و با داشتن اشاره‌گری به آن‌ها فراخوانی می‌کند.

# include <math.h>
# include <stdio.h>

/* Function taking a function pointer as an argument */
double compute_sum(double (*funcp)(double), double lo, double hi)
{
    double  sum = 0.0;

    /* Add values returned by the pointed-to function '*funcp' */
    for (int i = 0;  i <= 100;  i++)
    {
        double  x, y;

        /* Use the function pointer 'funcp' to invoke the function */
        x = i/100.0 * (hi - lo) + lo;
        y = (*funcp)(x);
        sum += y;
    }
    return (sum/100.0);
}

int main(void)
{
    double  (*fp)(double);      /* Function pointer */
    double  sum;

    /* Use 'sin()' as the pointed-to function */
    fp = sin;
    sum = compute_sum(fp, 0.0, 1.0);
    printf("sum(sin): %f\n", sum);

    /* Use 'cos()' as the pointed-to function */
    fp = cos;
    sum = compute_sum(fp, 0.0, 1.0);
    printf("sum(cos): %f\n", sum);
    return 0;
}

منابع 

انواع داده
زیرروال‌ها