مؤشرات الوظائف في برمجة C مع أمثلة
توفر المؤشرات إمكانيات كبيرة لوظائف "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
يقوم البرنامج بتبديل قيم المتغيرات الفعلية لأن الوظيفة تصل إليها عن طريق العنوان باستخدام مؤشر. سنناقش هنا عملية البرنامج:
- نعلن أن الدالة مسؤولة عن تبديل القيمتين المتغيرتين، والتي تأخذ مؤشرين صحيحين كمعلمات وترجع أي قيمة عند استدعائها.
- في الوظيفة الرئيسية، نعلن ونقوم بتهيئة متغيرين صحيحين ('m' و'n') ثم نطبع قيمهما على التوالي.
- نحن نطلق على وظيفة المبادلة () عن طريق تمرير عنوان المتغيرين كوسائط باستخدام رمز علامة الضم. بعد ذلك، نقوم بطباعة القيم الجديدة للمتغيرات.
- هنا نقوم بتعريف محتوى دالة swap() والتي تأخذ عنوانين لمتغيرين صحيحين كمعلمات ونعلن عن متغير صحيح مؤقت يستخدم كصندوق تخزين ثالث لحفظ أحد متغيرات القيمة والذي سيتم وضعه في المتغير الثاني.
- احفظ محتوى المتغير الأول المشار إليه بالحرف "a" في المتغير المؤقت.
- قم بتخزين المتغير الثاني الذي يشير إليه b في المتغير الأول الذي يشير إليه a.
- تحديث المتغير الثاني (المشار إليه بـ b) بقيمة المتغير الأول المحفوظ في المتغير المؤقت.
وظائف مع معلمات الصفيف
في لغة C، لا يمكننا تمرير مصفوفة من حيث القيمة إلى دالة. حيث أن اسم المصفوفة هو مؤشر (عنوان)، لذلك نقوم فقط بتمرير اسم مصفوفة إلى دالة مما يعني تمرير مؤشر إلى المصفوفة.
على سبيل المثال، نأخذ في الاعتبار البرنامج التالي:
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
هنا سوف نقوم بشرح كود البرنامج مع تفاصيله
- نعلن ونحدد وظيفة 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
وهنا سوف نناقش تفاصيل البرنامج
- نقوم بتعريف وإعلان دالة تقوم بإرجاع عنوان مصفوفة تحتوي على قيمة عددية ولا تأخذ أي وسيطات.
- نعلن عن مؤشر عدد صحيح يستقبل المصفوفة الكاملة التي تم إنشاؤها بعد استدعاء الوظيفة ونطبع محتوياتها عن طريق تكرار مصفوفة العناصر الخمسة بالكامل.
لاحظ أنه تم تعريف مؤشر، وليس مصفوفة، لتخزين عنوان المصفوفة التي أرجعتها الدالة. لاحظ أيضًا أنه عند إرجاع متغير محلي من دالة، علينا أن نعلن أنه ثابت في الدالة.
مؤشرات الوظيفة
كما نعلم بحكم التعريف أن المؤشرات تشير إلى عنوان في أي موقع بالذاكرة، ويمكنها أيضًا الإشارة إلى بداية التعليمات البرمجية القابلة للتنفيذ كوظائف في الذاكرة.
يتم الإعلان عن مؤشر الدالة باستخدام العلامة *، والبيان العام لإعلانه هو:
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
- نحن نحدد ونعلن عن وظيفة قياسية تطبع نص Hi k مرات تشير إليها المعلمة مرات عند استدعاء الوظيفة
- نحدد دالة المؤشر (مع إعلانها الخاص) والتي تأخذ معلمة عددية ولا تُرجع أي شيء.
- نقوم بتهيئة وظيفة المؤشر باستخدام وظيفة Hi_function مما يعني أن المؤشر يشير إلى وظيفة Hi_function().
- بدلاً من استدعاء الدالة القياسية عن طريق النقر على اسم الدالة مع الوسائط، فإننا نستدعي دالة المؤشر فقط عن طريق تمرير الرقم 3 كوسيطات، وهذا كل شيء!
ضع في اعتبارك أن اسم الوظيفة يشير إلى عنوان البداية للتعليمة البرمجية القابلة للتنفيذ مثل اسم المصفوفة الذي يشير إلى العنصر الأول الخاص بها. ولذلك، فإن التعليمات مثل function_ptr = &Hi_function و(*funptr)(3) صحيحة.
ملاحظة: ليس من المهم إدراج عامل العنوان & وعامل الإحالة غير المباشرة * أثناء تعيين الوظيفة واستدعاء الوظيفة.
مجموعة من المؤشرات الوظيفية
يمكن لمجموعة من المؤشرات الوظيفية أن تلعب دور التبديل أو عبارة 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
هنا نناقش تفاصيل البرنامج:
- نعلن ونحدد أربعة وظائف والتي تأخذ وسيطتين صحيحتين وترجع قيمة عددية. تقوم هذه الوظائف بإضافة وطرح وضرب وتقسيم الوسيطتين المتعلقتين بالوظيفة التي يستدعيها المستخدم.
- نعلن عن 4 أعداد صحيحة للتعامل مع المتغيرات ونوع العملية والنتيجة على التوالي. كما نعلن عن مصفوفة من أربعة مؤشرات دالة. يأخذ كل مؤشر دالة لعنصر المصفوفة معاملين صحيحين ويعيد قيمة عددية صحيحة.
- نقوم بتعيين كل عنصر من عناصر المصفوفة وتهيئته بالدالة المعلنة بالفعل. على سبيل المثال، سيشير العنصر الثالث الذي يمثل مؤشر الدالة الثالثة إلى دالة عملية الضرب.
- نحن نبحث عن المتغيرات ونوع العملية من المستخدم الذي يكتب باستخدام لوحة المفاتيح.
- قمنا بتسمية عنصر المصفوفة المناسب (مؤشر الدالة) باستخدام الوسائط، وقمنا بتخزين النتيجة التي تم إنشاؤها بواسطة الدالة المناسبة.
التعليمات int (*ope[4])(int, int); يحدد مجموعة مؤشرات الوظيفة. يجب أن يكون لكل عنصر صفيف نفس المعلمات ونوع الإرجاع.
نتيجة العبارة = ope[choice](x, y); يقوم بتشغيل الوظيفة المناسبة وفقًا لاختيار المستخدم. العددان الصحيحان اللذان تم إدخالهما هما الوسيطات التي تم تمريرها إلى الوظيفة.
وظائف باستخدام مؤشرات باطلة
يتم استخدام المؤشرات الفارغة أثناء إعلانات الوظائف. نحن نستخدم تصاريح نوع الإرجاع الفارغة * لإرجاع أي نوع. إذا افترضنا أن معلماتنا لا تتغير عند المرور إلى دالة، فإننا نعلنها على أنها const.
فمثلا:
void * cube (const 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
هنا سوف نناقش تفاصيل البرنامج:
- نقوم بتعريف وإعلان دالة تقوم بإرجاع قيمة عددية وتأخذ عنوان متغير غير قابل للتغيير بدون نوع بيانات محدد. نحسب القيمة المكعبة لمتغير المحتوى (x) الذي يشير إليه مؤشر num، وبما أنه مؤشر فارغ، علينا أن نكتب cast إلى نوع بيانات صحيح باستخدام مؤشر تدوين محدد (* datatype)، ونعود قيمة المكعب.
- نعلن عن المتغير المتغير والنتيجة. كما نقوم بتهيئة المتغير المتغير بالقيمة "4".
- نقوم باستدعاء دالة المكعب عن طريق تمرير عنوان المتغير، ونقوم بمعالجة القيمة المرتجعة في متغير النتيجة
مؤشرات الوظيفة كوسائط
هناك طريقة أخرى لاستغلال مؤشر دالة عن طريق تمريرها كوسيطة إلى دالة أخرى تسمى أحيانًا "وظيفة رد الاتصال" لأن الوظيفة المتلقية "تستدعيها مرة أخرى".
في ملف الرأس stdlib.h، تستخدم وظيفة Quicksort “qsort()” هذه التقنية وهي خوارزمية مخصصة لفرز مصفوفة.
void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
- void *base : مؤشر فارغ للمصفوفة.
- size_t num : رقم عنصر المصفوفة.
- size_t width حجم العنصر.
- int (*compare (const void *, const void *) : يتكون مؤشر الدالة من وسيطتين ويعيد 0 عندما تكون الوسيطات لها نفس القيمة، و<0 عندما يأتي arg1 قبل arg2، و>0 عندما يأتي arg1 بعد arg2.
يقوم البرنامج التالي بفرز مجموعة الأعداد الصحيحة من رقم صغير إلى رقم كبير باستخدام الدالة 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
هنا سوف نناقش تفاصيل البرنامج:
- نحدد دالة المقارنة المكونة من وسيطتين ونعيد 0 عندما تكون الوسيطات لها نفس القيمة، و<0 عندما يأتي arg1 قبل arg2، و>0 عندما يأتي arg1 بعد arg2. المعلمات هي نوع مؤشرات فارغة يتم إرسالها إلى نوع بيانات المصفوفة المناسب (عدد صحيح)
- نقوم بتعريف وتهيئة مصفوفة عدد صحيح، ويتم تخزين حجم المصفوفة في ملف NUM متغير ويتم تخزين حجم كل عنصر من عناصر المصفوفة في متغير العرض باستخدام sizeof() المحدد مسبقًا مشغل C.
- نطلق عليه com.qsort وظيفة وتمرير اسم المصفوفة وحجمها وعرضها ووظيفة المقارنة المحددة مسبقًا من قبل المستخدم من أجل فرز المصفوفة الخاصة بنا بترتيب تصاعدي. سيتم إجراء المقارنة عن طريق أخذ عنصرين من المصفوفة في كل تكرار حتى يتم فرز المصفوفة بأكملها.
- نقوم بطباعة عناصر المصفوفة للتأكد من أن المصفوفة الخاصة بنا مرتبة جيدًا عن طريق تكرار المصفوفة بأكملها باستخدام لحلقة.







