اشاره گرهای تابع در برنامه نویسی C

در این جلسه اشاره گرهای تابع در برنامه نویسی C را بررسی میکنیم. برای توابع C که محدود به ازگرداندن تنها یک مقدار هستند، اشاره گرها امکانات زیادی فراهم می آورند. با استفاده از پارامترهای اشاره گری(pointer parameters)، توابع به جای پردازش کپی داده ها، خود داده های واقعی را پردازش می کنند.

برای ویرایش مقادیر واقعی متغیرها، دستور فراخواننده، یک آدرس را به پارامترهای اشاره گری تابع ارسال می کند.

در این مبحث خواهیم آموخت:

  • مثال از اشاره گرهای تابع
  • توابع با پارامترهای آرایه ای
  • توابعی که یک آرایه برمی گردانند.
  • آرایه اشاره گرهای تابعی
  • توابعی که از اشاره گرهای void استفاده می کنند.
  • اشاره گر تابعی به عنوان آرگومان

قبل از ادامه شدیدا توصیه می‌کنم مقاله اشاره گر ها در زبان C همراه با مثال موجود در وب‌سایت میکرو دیزاینر الکترونیک را مطالعه کنید و بعد ادامه مسیر را طی کنید. البته اگر با موضوع اشاره‌گرها آشنا هستید نیازی نیست و مطالعه مقاله را ادامه دهید.

مثالی از اشاره گرهای تابعی

برای مثال، برنامه زیر دو مقدار را با هم معاوضه می کند:

void swap (int *a, int *b);

int main() {

int m = 25;

int n = 100;

printf("m is %d, n is %d\n", m, n);

swap(&m, &n);

printf("m is %d, n is %d\n", m, n);

return 0;}

void swap (int *a, int *b) {

int temp;

temp = *a;

*a = *b;

*b = temp;}

}

خروجی

m is 25, n is 100

m is 100, n is 25

اشاره گرهای تابع در برنامه نویسی C

از آنجایی که تابع با استفاده از اشاره گرها به آدرس متغیرها دسترسی دارد، برنامه مقدار واقعی دو متغیر را معاوضه می کند. فرآیند برنامه را در زیر توضیح می دهیم:

  • تابع مورد نیاز برای جابجاکردن مقدار دو متغیر را اعلان می کنیم، این دوتابع دو اشاره گر integer به عنوان پارامتر دریافت کرده و هیچ مقداری هنگام فراخوانی برنمی گرداند.
  • در تابع main، دو متغیر (‘m’ و ‘n’) از نوع Integer  را اعلان کرده و سپس مقادیر آنها را به ترتیب چاپ می کنیم.
  • تابع ()swap را با ارسال آدرس دو متغیر(با استفاده از &) به عنوان آرگومان فراخوانی می کنیم. پس از آن، مقادیر معاوضه شده جدید متغیرها را چاپ می کنیم.
  • محتوای تابع ()swap را تعریف می کنیم، این تابع آدرس متغیر integer را به عنوان پارامتر دریافت می کند. یک متغیرصحیح موقت به عنوان ظرف سوم اعلان می کنیم. این متغیر برای ذخیره مقدار یکی از متغیرها که در متغیر دوم جایگزین می شود بکار می رود.
  • متغیر دوم مورد اشاره a در متغیر اول مورد اشاره b ذخیره می شود.
  • متغیر دوم(مورد اشاره b) را با مقدار متغیر اول که در متغیر موقت ذخیره شده بود بروزرسانی می‌کنیم.

توابع با پارامترهای آرایه ای

در زبان C، نمی توان یک آرایه را به صورت ارسال با مقدار(pass by value) به یک تابع فرستاد. از آنجایی که نام آرایه یک اشاره گر(آدرس) است، بنابراین تنها نام آرایه را به تابع ارسال می کنیم که به منزله ارسال اشاره گر آرایه می باشد.

برنامه زیر را به عنوان مثال ملاحظه کنید:

int add_array (int *a, int num_elements);

int main() {

int Tab[5] = {100, 220, 37, 16, 98};

printf("Total summation is %d\n", add_array(Tab, 5));

return 0;}

int add_array (int *p, int size) {

int total = 0;

int k;

for (k = 0; k < size; k++) {

total += p[k]; /* it is equivalent to total +=*p ;p++; */}

return (total);}

خروجی:

Total summation is 471

کد برنامه را با همراه با جزئیات در زیر شرح می دهیم:

مطلب پیشنهادی:  دستور Switch Case در زبان C همراه با مثال

اشاره گرهای تابع در برنامه نویسی C

تابع ()add_array را اعلان و تعریف می کنیم. این تابع آدرس(اشاره گر) آرایه و تعداد عناصر آن را به عنوان پارامتر دریافت کرده و حاصل جمع این عناصر را برمی گرداند. اشاره گر مذکور برای انجام عمل شمارش عناصر آرایه(با استفاده از [p[k) استفاده می شود. حاصل جمع در یک متغیر محلی ذخیره شده و بعد از شمارش کل عناصر برگردانده می شود.

حاصل جمع را با ارسال نام آرایه(که به منزله آدرس عمل کرده) و اندازه آرایه به عنوان آرگومان تابع ()add_array چاپ می کنیم.

توابعی که یک آرایه برمی‌گردانند

در زبان C همانند برنامه زیر می توان یک اشاره گر آرایه را برگرداند:

#include <stdio.h>

int * build_array();

int main() {

int *a;

a = build_array(); /* get first 5 even numbers */

for (k = 0; k < 5; k++)

printf("%d\n", a[k]);

return 0;}

int * build_array() {

static int Tab[5]={1,2,3,4,5};

return (Tab);}

خروجی:

1

2

3

4

5

جزئیات برنامه را در زیر شرح خواهیم داد:

اشاره گرهای تابع در برنامه نویسی C

  1. تابعی تعریف و اعلان می کنیم که آدرس یک آرایه شامل یک مقدار integer را برگردانده و هیچ آرگومانی را دریافت نمی کند.
  2. یک اشاره گر Integer را اعلان می کنیم.این اشاره گر پس از فراخوانی تابع،آرایه کامل شده را دریافت می کند. با شمارش روی پنج عنصر آرایه مقادیر آن را چاپ می کنیم.

دقت کنید که برای ذخیره آدرس برگردانده شده توسط تابع، یک اشاره گر، و نه یک آرایه، تعریف می شود. همچنین توجه کنید که زمانی که یک متغیر محلی از تابع برگردانده می شود، می توانیم آن را در تابع به صورت ایستا(static) تعریف کنیم.

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

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

اشاره گر به تابع همراه با * اعلان می شود، عبارت کلی برای آن به صورت زیر است:

return_type (*function_name)(arguments)

باید به خاطر داشته باشید که پرانتزهای عبارت (*function_name) مهم هستند زیرا بدون آنها، کامپایلر فکر می کند که function_name اشاره گری به return_type برمی گرداند.

پس از تعریف اشاره گر تابع، باید آن را به یک تابع اختصاص بدهیم.برای مثال، برنامه زیر یک تابع معمولی را اعلان کرده، یک اشاره گر تابعی تعریف کرده و اشاره گر تابعی را به تابع معمولی اختصاص می دهد و پس از آن تابع را از طریق اشاره گر فراخوانی می کند:

#include <stdio.h>

void Hi_function (int times); /* function */

int main() {

void (*function_ptr)(int); /* function pointer Declaration */

function_ptr = Hi_function; /* pointer assignment */

function_ptr (3); /* function call */

return 0;}

void Hi_function (int times) {

int k;

for (k = 0; k < times; k++) printf("Hi\n");}

خروجی:

Hi

Hi

Hi

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

  1. یک تابع استاندارد اعلان و تعریف می کنیم که متن Hi را به اندازه K بار چاپ کرده که مقدار K توسط پارامترtimes هنگام فراخوانی تابع مشخص می شود.
  2. یک اشاره گر تابعی(یک اعلان خاص خود را دارد) تعریف می کنیم که یک پارامتر integer دریافت کرده و هیچ مقداری برنمی گرداند.
  3. تابع اشاره گر خود را با Hi_function مقداردهی اولیه می کنیم. این بدان معناست که اشاره گر به تابع Hi_function() اشاره خواهد کرد.
  4. بجای فراخوانی تابع استاندارد از طریف نام تابع همراه با آرگومانها، تنها اشاره گر تابع را با ارسال عدد 3 به عنوان آرگومان فراخوانی می کنیم، به همین سادگی!
مطلب پیشنهادی:  اولین برنامه به زبان C - مثال !Hello World

توجه داشته باشید که همانند نام آرایه که به ادرس اولین عنصر اشاره می کند، نام تابع نیز به آدرس آغازین کد اجرایی اشاره می کند.

بنابراین، دستوراتی مانند function_ptr = &Hi_function و (3)(*funptr) صحیح هستند.

نکته: نیازی به قرار دادن عملگر آدرس & و عملگر غیرمستقیم * هنگام تخصیص و فراخوانی تابع نیست.

آرایه اشاره گرهای تابعی

آنچنان که در مثال زیر آمده، یک آرایه از اشاره گرهای تابعی می توانند نقش switch یا دستور if را برای فرآیند تصمیم گیری بازی کنند:

#include <stdio.h>

int sum(int num1, int num2);

int sub(int num1, int num2);

int mult(int num1, int num2);

int div(int num1, int num2);

int main()

{ int x, y, choice, result;

int (*ope[4])(int, int);

ope[0] = sum;

ope[1] = sub;

ope[2] = mult;

ope[3] = div;

printf("Enter two integer numbers: ");

scanf("%d%d", &x, &y);

printf("Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: ");

scanf("%d", &choice);

result = ope[choice](x, y);

printf("%d", result);

return 0;}

int sum(int x, int y) {return(x + y);}

int sub(int x, int y) {return(x - y);}

int mult(int x, int y) {return(x * y);}

int div(int x, int y) {if (y != 0) return (x / y); else return 0;}

خروجی

Enter two integer numbers: 13 48

Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: 2

624

شرح جزئیات برنامه در زیر آمده است:

آرایه اشاره گرهای تابعی

  1. چهار تابع را اعلان و تعریف می کنیم که دو آرگومان Integer گرفته و یک مقدار integer برمی گردانند.این توابع دو آرگومان ورودی را جمع، تفریق؛ ضریب و تقسیم می کنند.
  2. 4 متیغر integer به ترتیب برای عملوندها، نوع عملگر و نتیجه اعلان می کنیم. همچنین یک آرایه مشتکل از چهار اشاره گر تابعی اعلان می کنیم. هر اشاره گر تابعی دو پارامتر Integer دریافت کرده و یک مقدار integer برمی گرداند.
  3. هر عنصر آرایه را به توابع از پیش اعلان شده اختصاص می دهیم.برای مثال، عنصر سوم که سومین اشاره گر تابعی است به تابع حاصل ضرب اشاره می کند.
  4. عملوندها و نوع عملگر را از طریق صفحه کلید از کاربر درخواست می کنیم.
  5. عنصر آرایه مطلوب(اشاره گر تابع) همراه با آرگومان ها را فراخوانی کرده و نتیجه حاصله از تابع مطلوب را ذخیره می کنیم.

دستور ؛(int(*ope[4])(int,int آرایه اشاره گرهای تابعی را تعریف می کند. هر عنصر آرایه باید پارامترها و نوع داده بازگشتی یکسان داشته باشد.

دستور ؛(result = ope[choice](x, y تابع مناسب با انتخاب کاربر را اجرا می کند. دو عدد integer وارد شده آرگومانهای ارسالی به تابع هستند.

توابعی که از اشاره گر void استفاده می کنند.

اشاره گرهای void هنگام اعلان توابع بکار می روند. از void * به عنوان نوع داده بازگشتی استفاده می کنیم تا امکان بازگرداندن هر نوع داده ای را فراهم کنیم. اگر بدانیم که پارامترها هنگام ارسال به تابع تغییر نخواهند کرد، آنها را به صورت ثابت اعلان می کنیم.

به عنوان مثال:

#include <stdio.h>

void* cube (const void* num);

int main() {

int x, cube_int;

x = 4;

cube_int = cube (&x);

printf("%d cubed is %d\n", x, cube_int);

return 0;}

void* cube (const void *num) {

int result;

result = (*(int *)num) * (*(int *)num) * (*(int *)num);

return result;}

نتیجه:

4 cubed is 64

جزئیات برنامه در زیر شرح داده است:

اشاره گر void

  1. تابعی تعریف و اعلان می کنیم که یک مقدار integer برمی گرداند. این تابع آدرس یک متغیر تغییرناپذیرو بدون نوع داده مشخص را دریافت می کند.مکعب یک متغیر ثابت (x ) که اشاره گر num به آن اشاره می کند را حساب می کنیم. از آنجایی که اشاره گر void است، باید با استفاده از عبارت ویژه (*datatype) آن را به یک نوع داده integer تبدیل نوع(typecast) کنیم، و سپس مقدار مکعب را برمی گردانیم.
  2. عملوند و متیغر نتیجه را اعلان می کنیم. همچنین، عملوند را با مقدار “4” مقداردهی اولیه می کنیم.
  3. تابع cube را با ارسال آدرس عملوند فراخوانی کرده، و مقدار بازگشتی را در متغیر نتیجه قرار می دهیم.
مطلب پیشنهادی:  آموزش کار با CMake

اشاره گرهای تابعی به عنوان آرگومان

روش دیگر برای استفاده از اشاره گر تابعی ارسال آن به عنوان آرگومان به توابع دیگر است این تابع گاهی تابع “callback function” نامیده می شود زیرا تابع دریافت کننده “آن را برمی گرداند.”

تابع مرتب سازی سریع “()qsort” در هدرفایل stdlib.h، از این تکنیک استفاده می کند. این تکنیک الگوریتمی است که برای مرتب سازی یک آرایه بکار می رود.

void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
  • void *base: اشاره گر void به آرایه
  • size_t num: شماره عنصر آرایه
  • size_t width: اندازه عنصر
  • int (*compare (const void *, const void*) : تابع اشاره گر متشکل از دو آرگومان است و زمانی که آرگومانها مقدار یکسان داشته باشند مقدار 0، اگر arg1 پیش از arg2 بیاید بزگتر از 0 و اگر arg1 پس از arg2 بیاید کمتر از 0 برمی گرداند.

برنامه زیر یک آرایه integer را با استفاده از تابع ()qsort از عدد کوچک به بزرگ مرتب سازی می کند.

#include <stdio.h>

#include <stdlib.h>

int compare (const void *, const void *);

int main() {

int arr[5] = {52, 14, 50, 48, 13};

int num, width, i;

num = sizeof(arr)/sizeof(arr[0]);

width = sizeof(arr[0]);

qsort((void *)arr, num, width, compare);

for (i = 0; i < 5; i++)

printf("%d ", arr[ i ]);

return 0;}

int compare (const void *elem1, const void *elem2) {

if ((*(int *)elem1) == (*(int *)elem2)) return 0;

else if ((*(int *)elem1) < (*(int *)elem2)) return -1;

else return 1;}

خروجی:

13 14 48 50 52

اشاره گرهای تابعی به عنوان آرگومان

  1. تابع compare را به گونه ای تعریف می کنیم که دو آرگومان دریافت کند،و اگر آرگومان ها مقدار یکسان داشته باشند عدد 0، اگر arg1 پیش از arg2 بیاید کوچکتر از 0، و اگر arg1 پس از arg2 بیاید بزرگتر از 0 برمی گرداند. پارامترها اشاره گرهای void هستند که به آرایه نوع داده مناسب (integer) تبدیل نوع(type cast) شده اند.
  2. یک آرایه Integer را اعلان و مقداردهی اولیه کرده ایم. اندازه آرایه درون متغیر num ذخیره شده و اندازه هر عنصر آرایه با استفاده از عملگر ()sizeof درون متغیر width ذخیره می گردد.
  3. تابع qsort را فراخوانی کرده و نام آرایه، اندازه ، width و تابع مقایسه که قبلاً توسط کاربر، برای مرتب سازی صعودی آرایه تعریف شده بود را به آن ارسال می کنیم.مقایسه با تکرار روی جفت عنصر آرایه انجام شده تا کل آرایه ذخیره شود.
  4. عناصر آرایه را چاپ می کنیم تا مطمئن شویم که با شمارش روی کل آرایه با استفاده از حلقه for، آرایه به درستی مرتب شده است.

اگر این نوشته‌ برایتان مفید بود لطفا کامنت بنویسید.

مطالعه دیگر جلسات این آموزش<< جلسه قبلی                    جلسه بعدی >>

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *