C++: أساسيات الدوال

آخر تحديث: 2026-08-26

في الدروس الساببة، كان كل الكود مكتوبًا داخل دالة main.

لكن مع نمو البرامج، تصبح دالة main طويلة وصعبة الصيانة.

الدوال تحل هذه المشكلة — تقسيم الكود إلى وحدات صغيرة، كل مسؤولة عن مهمة واحدة.


1. ما هي الدالة؟

(1) 1.1 الدوال في الحياة اليومية

سيناريو من الحياة المقابل البرمجي
تضغط زر "التشغيل" على جهاز التحكم بالتلفاز استدعاء دالة turnOnTV()
التلفاز ينفذ تلقائيًا سلسلة عمليات (تشغيل، بحث القنوات، عرض الصورة) الكود داخل جسم الدالة
لا تحتاج لمعرفة كيف يعمل التلفاز داخليًا خاصية "التغليف" للدوال

الدالة هي كتلة كود تنفذ مهمة محددة، يمكن استدعاؤها بشكل متكرر.

(2) 1.2 لماذا نحتاج الدوال؟

بدون دوال (مثال سيئ):

TEXT 📖 للعرض فقط
#include <iostream>

int main() {
 // Calculate area of the first rectangle
 double width1 = 5.0, height1 = 3.0;
 double area1 = width1 * height1;
 std::cout << "Area 1: " << area1 << std::endl;
 
 // Calculate area of the second rectangle
 double width2 = 7.0, height2 = 2.5;
 double area2 = width2 * height2;
 std::cout << "Area 2: " << area2 << std::endl;
 
 // Calculate area of the third rectangle
 double width3 = 4.0, height3 = 6.0;
 double area3 = width3 * height3;
 std::cout << "Area 3: " << area3 << std::endl;
 
 return 0;
}

بالدوال (مثال جيد):

CPP
#include <iostream>

// Define the function
double calculateArea(double width, double height) {
 return width * height;
}

int main() {
 std::cout << "Area 1: " << calculateArea(5.0, 3.0) << std::endl;
 std::cout << "Area 2: " << calculateArea(7.0, 2.5) << std::endl;
 std::cout << "Area 3: " << calculateArea(4.0, 6.0) << std::endl;
 
 return 0;
}

مقارنة:

بدون دوال بدوال
تكرار الكود (نسخ ولصق) إعادة استخدام الكود (اكتب مرة واحدة)
تغيير المنطق يتطلب تغييرات في أماكن متعددة غيّر جسم الدالة في مكان واحد فقط
صعوبة الصيانة سهولة الصيانة


2. تعريف الدالة

(1) 2.1 الصياغة الأساسية

TEXT 📖 للعرض فقط
ReturnType functionName(parameterList) {
 // function body
 return returnValue; // if return type is not void
}

(2) 2.2 أجزاء الدالة

الجزء المعنى المثال
نوع الإرجاع نوع القيمة التي تُرجعها الدالة int، double، void (بدون إرجاع)
اسم الدالة اسم الدالة (يتبع قواعد تسمية المتغيرات) calculateArea، printHello
قائمة المعلمات المدخلات التي تستقبلها الدالة (يمكن أن تكون فارغة) (double w, double h)
جسم الدالة الكود الذي تنفذه الدالة { return w * h; }
return يُرجع النتيجة إلى المستدعي return area;

▶ مثال 1: تعريف دالة بسيطة (صعوبة ⭐)

CPP
#include <iostream>

// Function definition: calculate the sum of two numbers
int add(int a, int b) {
 return a + b;
}

int main() {
 int result = add(3, 5); // Call the function
 std::cout << "3 + 5 = " << result << std::endl;
 return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
3 + 5 = 8


3. استدعاء الدوال

(1) 3.1 صياغة الاستدعاء

TEXT 📖 للعرض فقط
functionName(argumentList);
المصطلح المعنى المثال
تعريف الدالة كتابة كود الدالة int add(int a, int b) { ... }
استدعاء الدالة استخدام الدالة add(3, 5);
المعامل الشكلي (parameter) المعلمات في تعريف الدالة int a, int b
المعامل الفعلي (argument) القيم الممررة عند استدعاء الدالة 3, 5

💡 نقطة مهمة: المعاملات الفعلية تُنسخ إلى المعاملات الشكلية (سنتعلم عن المراجع لاحقًا، حيث لا حاجة للنسخ).



4. قيم الإرجاع

(1) 4.1 جملة return

return له غرضان:

  1. إرجاع نتيجة إلى المستدعي
  2. إنهاء الدالة (الكود بعد return لن يُنفَّذ)
TEXT 📖 للعرض فقط
#include <iostream>

int max(int a, int b) {
 if (a > b) {
 return a; // End function, return a
 } else {
 return b; // End function, return b
 }
 std::cout << "This line never executes" << std::endl; // Dead code
}

int main() {
 std::cout << "Maximum: " << max(10, 20) << std::endl;
 return 0;
}

(2) 4.2 الدوال بدون قيمة إرجاع (void)

إذا لم تكن الدالة بحاجة لإرجاع قيمة، استخدم void كنوع الإرجاع:

CPP
#include <iostream>

void printWelcome() { // void means no return value
 std::cout << "========== Welcome ==========" << std::endl;
 std::cout << "This is a void function, no return needed" << std::endl;
}

int main() {
 printWelcome(); // Call void function, no need to receive a return value
 return 0;
}

💡 نصيحة: يمكنك أيضًا كتابة return; (بدون قيمة) في دالة void للخروج مبكرًا.

TEXT 📖 للعرض فقط
void printIfPositive(int x) {
 if (x <= 0) {
 return; // Exit the function early
 }
 std::cout << x << " is positive" << std::endl;
}


5. النماذج الأولية للدوال (تصريحات الدوال)

(1) 5.1 المشكلة: يجب تعريف الدوال قبل استدعائها

CPP
#include <iostream>

int main() {
 std::cout << add(3, 5) << std::endl; // ❌ Error: compiler hasn't seen add's definition yet
 return 0;
}

int add(int a, int b) {
 return a + b;
}

رسالة خطأ الترجمة:

TEXT 📖 للعرض فقط
error: 'add' was not declared in this scope

(2) 5.2 الحل: النماذج الأولية للدوال

النموذج الأولي للدالة (يسمى أيضًا تصريح الدالة) يخبر المترجم "هذه الدالة موجودة"، بينما يمكن تعريف جسم الدالة الفعلي لاحقًا.

TEXT 📖 للعرض فقط
#include <iostream>

int add(int a, int b); // Function prototype (ends with semicolon!)

int main() {
 std::cout << add(3, 5) << std::endl; // ✅ Compiler knows add exists
 return 0;
}

int add(int a, int b) { // Function definition
 return a + b;
}

💡 القواعد:



6. تطبيق: آلة حاسبة بالدوال

▶ مثال 2: آلة حاسبة باستخدام الدوال (صعوبة ⭐⭐)

CPP 📖 للعرض فقط
#include <iostream>
#include <iomanip>

// Function prototypes
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
double divide(double a, double b);

int main() {
 double num1, num2;
 char op;
 
 std::cout << "Please enter the first number: ";
 std::cin >> num1;
 
 std::cout << "Please enter an operator (+ - * /): ";
 std::cin >> op;
 
 std::cout << "Please enter the second number: ";
 std::cin >> num2;
 
 std::cout << std::fixed << std::setprecision(2);
 
 switch (op) {
 case '+':
 std::cout << "Result: " << add(num1, num2) << std::endl;
 break;
 case '-':
 std::cout << "Result: " << subtract(num1, num2) << std::endl;
 break;
 case '*':
 std::cout << "Result: " << multiply(num1, num2) << std::endl;
 break;
 case '/':
 if (num2 != 0) {
 std::cout << "Result: " << divide(num1, num2) << std::endl;
 } else {
 std::cout << "Error: Divisor cannot be 0!" << std::endl;
 }
 break;
 default:
 std::cout << "Error: Unsupported operator!" << std::endl;
 break;
 }
 
 return 0;
}

// Function definitions
double add(double a, double b) {
 return a + b;
}

double subtract(double a, double b) {
 return a - b;
}

double multiply(double a, double b) {
 return a * b;
}

double divide(double a, double b) {
 return a / b;
}
49 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

الخرج:

TEXT 📖 للعرض فقط
Please enter the first number: 
Please enter an operator (+ - * /): 
Please enter the second number: 
Result: 
Result: 
Result: 
Result: 
Error: Divisor cannot be 0!
Error: Unsupported operator!


7. الأخطاء الشائعة

(1) 7.1 نسيان كتابة نموذج أولي للدالة

خطأ:

TEXT 📖 للعرض فقط
int main() {
 foo(); // ❌ Compiler doesn't know what foo is yet
 return 0;
}

void foo() {
 std::cout << "Hello" << std::endl;
}

إصلاح: أضف نموذجًا أوليًا للدالة:

CPP
void foo(); // ✅ Function prototype

int main() {
 foo();
 return 0;
}

void foo() {
 std::cout << "Hello" << std::endl;
}

(2) 7.2 عدم تطابق النموذج الأولي مع التعريف

خطأ:

TEXT 📖 للعرض فقط
int foo(int a); // Prototype: returns int

int main() {
 foo(5);
 return 0;
}

double foo(int a) { // ❌ Error: definition returns double, doesn't match prototype
 return a + 0.5;
}

(3) 7.3 نسيان كتابة return

خطأ:

CPP
int add(int a, int b) {
 int sum = a + b;
 // ❌ Forgot to write return
}

int main() {
 int result = add(3, 5);
 std::cout << result << std::endl; // Outputs garbage value
 return 0;
}

💡 نصيحة: قد لا يحذرك مترجم C++ من return مفقود، لكن سلوك البرنامج غير معرف (قد يُخرج قيم عشوائية، قد ينهار).


❓ أسئلة شائعة

س هل يمكن حذف أسماء المعلمات في النماذج الأولية للدوال؟
ج نعم، لكن يُنصح بالاحتفاظ بها. int add(int, int); صالح نحويًا؛ int add(int a, int b); موصى به لسهولة القراءة.
س هل يمكن للدالة إرجاع قيم متعددة؟
ج دالة C++ يمكن أن يكون لها قيمة إرجاع واحدة فقط. لكن يمكنك "إرجاع" قيم متعددة باستخدام: 1. معلمات مؤشر أو مرجع (ستُغطى لاحقًا) 2. إرجاع بنية أو صنف (ستُغطى لاحقًا) 3. استخدام std::tuple (C++11 وما بعدها)
س هل يمكنني كتابة return في دالة void؟
ج نعم! return; (بدون قيمة) يمكنه الخروج من دالة void مبكرًا.
س لماذا دالتي المُعرَّفة بعد main تُترجَم بنجاح؟
ج قد يكون المترجم "متساهلًا"، أو ربما كتبت نموذجًا أوليًا للدالة دون قصد. التوصية: ضع دائمًا النماذج الأولية للدوال قبل main والتعريفات بعد (أو كلاهما قبل).

▶ مثال 3: قيمة إرجاع الدالة (صعوبة ⭐)

CPP
#include <iostream>

int max(int a, int b) {
    return (a > b) ? a : b;
}

int main() {
    int x = 10, y = 20;
    std::cout << "Larger value: " << max(x, y) << std::endl;

    return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
Larger value: 20
💡 نصيحة: الدوال يمكنها إرجاع نتائج محسوبة باستخدام return. العامل الثلاثي ? : يمكنه تبسيط الفحوص الشرطية البسيطة.


📖 ملخص


📝 تمارين

  1. أساسي (صعوبة ⭐): اكتب دالة int multiply(int a, int b) تُرجع جداء عددين صحيحين. استدعِها في دالة main.

  2. متوسط (صعوبة ⭐⭐): اكتب دالة bool isPrime(int n) تحدد هل العدد الصحيح أولي.

  3. عدد أولي: عدد قابل للقسمة فقط على 1 وعلى نفسه (مثل 2، 3، 5، 7، 11)

  4. في دالة main، دع المستخدم يدخل عددًا صحيحًا، استدعِ isPrime، وأخرج النتيجة

  5. تحدي (صعوبة ⭐⭐⭐): اكتب برنامجًا يحتوي الدوال التالية:

  6. int fibonacci(int n): تُرجع العدد الفيبوناتشي رقم n

  7. void printFibonacci(int n): تُخرج أول n عدد فيبوناتشي

في دالة main، دع المستخدم يدخل n، أولًا أخرج الأعداد n الأولى، ثم أخرج قيمة العدد رقم n.


8. 🚀 الخطوة التالية

الآن بعد أن تعلمت أساسيات الدوال، سنتعلم بعد ذلك تمرير معلمات الدوال (الدرس 11) — تعلم كيفية تمرير المعلمات إلى الدوال والفرق بين "التمرير بالقيمة" و"التمرير بالمرجع".

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%