الدوال في C: التعريف والنموذج والمؤشرات
البرامج الكبيرة لا تُكتب كتلة واحدة، بل تُقسَّم إلى دوال صغيرة، كل واحدة تؤدي مهمة واحدة واضحة. أنت تستخدم الدوال منذ أول درس: main دالة، و printf دالة. في هذا الدرس ستكتب دوالك الخاصة، وستتعرف على مفهوم من أشهر مفاهيم C وأهمها: المؤشرات، في أبسط صورها.
تعريف دالة
Section titled “تعريف دالة”#include <stdio.h>
int add(int a, int b) { return a + b;}
void greet(void) { printf("أهلاً بك في C!\n");}
int main(void) { greet(); printf("%d\n", add(3, 4)); return 0;}أهلاً بك في C!7intقبل الاسم: نوع القيمة التي تُرجعها الدالة.(int a, int b): المعاملات وأنواعها.return: تُرجع القيمة وتُنهي الدالة.voidكنوع مرجع: الدالة لا تُرجع شيئاً. وvoidبين القوسين: لا تستقبل معاملات.
وحتى main تُرجع int: القيمة 0 في return 0; تخبر نظام التشغيل أن البرنامج انتهى بنجاح.
النموذج الأولي (Prototype)
Section titled “النموذج الأولي (Prototype)”المترجم في C يقرأ الملف من الأعلى للأسفل. إذا استدعيت دالة قبل أن يراها، سيشتكي. الحل: اكتب النموذج الأولي للدالة في أعلى الملف، أي سطر توقيعها فقط منتهياً بفاصلة منقوطة:
#include <stdio.h>
int square(int x); /* النموذج الأولي */int isEven(int n);
int main(void) { printf("%d\n", square(5)); printf("%d\n", isEven(4)); printf("%d\n", isEven(7)); return 0;}
int square(int x) { return x * x;}
int isEven(int n) { return n % 2 == 0;}2510النموذج الأولي مثل فهرس الكتاب: يخبر المترجم مسبقاً بأسماء الدوال وما تستقبله وما تُرجعه، فيتعرف عليها عند استدعائها، ثم يجد تفاصيلها لاحقاً في الملف. وهذا يسمح لك بوضع main في الأعلى لتكون أول ما يقرؤه أي شخص يفتح الملف.
لاحظ أن isEven تُرجع 1 أو 0، فهكذا تمثّل C الصح والخطأ كما تعلمت في درس الشروط.
تمرير القيمة: نسخة فقط
Section titled “تمرير القيمة: نسخة فقط”void tryToChange(int x) { x = 100;}
int main(void) { int value = 5; tryToChange(value); printf("%d\n", value); return 0;}5لم تتغير القيمة! لأن C ترسل للدالة نسخة من القيمة. الدالة غيّرت نسختها الخاصة، والمتغير الأصلي في main بقي كما هو. مثل أن تعطي صديقك صورة من مفتاح بيتك على ورقة: يستطيع الرسم عليها، لكنه لا يستطيع فتح بيتك بها.
المؤشرات: تعديل المتغير الأصلي
Section titled “المؤشرات: تعديل المتغير الأصلي”ماذا لو أردت فعلاً أن تغيّر الدالة متغيراً في main؟ تعطيها عنوانه في الذاكرة بدلاً من نسخة من قيمته:
void addPoints(int *score, int bonus) { *score = *score + bonus;}
int main(void) { int score = 10; addPoints(&score, 5); printf("%d\n", score); return 0;}15ثلاثة رموز جديدة:
&score: «عنوان المتغيرscoreفي الذاكرة».int *score: المعامل مؤشر (Pointer)، أي متغير يحمل عنواناً لمكان فيهint.*score: «القيمة الموجودة عند هذا العنوان». فـ*score = ...تكتب في المتغير الأصلي نفسه.
تذكّر تشبيه الشارع من درس المتغيرات: الذاكرة صناديق مرقمة. بدلاً من أن تعطي الدالة نسخة مما في الصندوق، أعطيتها رقم الصندوق، فذهبت إليه وغيّرت ما بداخله.
دوال مكتبة math
Section titled “دوال مكتبة math”C تأتي مع مكتبة رياضيات جاهزة:
#include <math.h>
printf("%.1f\n", sqrt(16.0));printf("%.0f\n", pow(2, 10));4.01024sqrt للجذر التربيعي، و pow للأس، وكلاهما يُرجع double. وعلى بعض الأنظمة مثل Linux، تحتاج إضافة -lm في أمر الترجمة: gcc main.c -o main -lm.
جرّب بنفسك
Section titled “جرّب بنفسك”#include <stdio.h>
double priceAfterDiscount(double price, double percent);
int main(void) { printf("%.1f\n", priceAfterDiscount(200, 10)); printf("%.1f\n", priceAfterDiscount(200, 25)); return 0;}
double priceAfterDiscount(double price, double percent) { return price - price * percent / 100;}180.0150.0تحدٍّ: اكتب دالة void swap(int *a, int *b) تبدّل قيمتي متغيرين في main. تلميح: تحتاج متغيراً مؤقتاً.
أخطاء شائعة
Section titled “أخطاء شائعة”- استدعاء دالة قبل تعريفها بدون نموذج أولي: تحذير أو خطأ
implicit declaration of function. أضف النموذج في الأعلى. - نسيان & عند تمرير العنوان:
addPoints(score, 5)ترسل القيمة بدل العنوان، والمترجم يحذرك. - نسيان return في دالة غير void: تُرجع قيمة عشوائية، والمترجم مع
-Wallيحذرك.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ما فائدة النموذج الأولي int square(int x); في أعلى الملف؟
المترجم يقرأ من الأعلى للأسفل، والنموذج الأولي يخبره مسبقاً باسم الدالة وأنواع معاملاتها وقيمتها المرجعة.
سؤال
ماذا يعني &score في C؟
& تعطي عنوان المتغير، وتمريره لدالة تستقبل مؤشراً يسمح لها بتعديل المتغير الأصلي.
الخلاصة
Section titled “الخلاصة”- الدالة: نوع مرجع، واسم، ومعاملات بأنواعها، و
voidتعني لا شيء. - النموذج الأولي في أعلى الملف يسمح باستدعاء الدالة قبل تعريفها.
- C ترسل نسخة من القيم، فالدالة لا تغيّر المتغير الأصلي.
- المؤشرات:
&xعنوان المتغير، وint *pمؤشر، و*pالقيمة عند العنوان. math.hفيهاsqrtوpow، وقد تحتاج-lmعند الترجمة.