التمرير بالقيمة والمرجع (Pass by Value & Reference)

💡 الدرس الثاني عشر: التمرير بالقيمة والمرجع (Pass by Value & Pass by Reference) في C++

من أكثر المفاهيم اللي بتغيّر طريقة فهمك للغة C++ هو “كيف تتعامل الدوال مع المتغيرات”.
هل الدالة فعلاً بتغيّر القيمة الأصلية؟ ولا بس بتشتغل على نسخة مؤقتة؟
هون بيدخل موضوع التمرير بالقيمة والمرجع — واحد من أساسيات اللغة واللي بيأثر مباشرة على الأداء، الذاكرة، وطريقة تصميمك للدوال.


🎯 لماذا هذا الدرس مهم؟

خلينا نحكيها بصراحة: المبرمج اللي ما بيفهم الفرق بين التمرير بالقيمة والمرجع، بيكتب كود شكله شغال… لكنه مش فعلاً “فاهم شو بصير تحت الغطاء”.
التمرير بالقيمة والمرجع مش بس اختلاف بطريقة الكتابة، بل اختلاف بطريقة تعامل اللغة مع الذاكرة نفسها.


🧩 الفكرة العامة

كل مرة تستدعي فيها دالة في C++، اللغة عندها خيارين:

  1. إرسال نسخة من المتغير (Pass by Value)
  2. إرسال عنوان المتغير نفسه (Pass by Reference)

خلينا نبدأ من البداية ونفكك الفكرة خطوة بخطوة.


⚙️ التمرير بالقيمة (Pass by Value)

🔹 المفهوم:

عندما تمرر متغيرًا إلى دالة بهذه الطريقة، فإن C++ تُنشئ نسخة جديدة منه داخل الدالة.
أي تعديل على النسخة داخل الدالة لن يؤثر على المتغير الأصلي في main().

🔹 كيف تعمل في الذاكرة؟

كل دالة تستدعى، تنشأ لها “إطار” داخل Stack Memory يحتوي على:

  • نسخة من المتغيرات الممررة.
  • متغيرات محلية خاصة بها.

بمجرد انتهاء تنفيذ الدالة، يتم حذف هذا الإطار بالكامل.

🔹 مثال بسيط:

#include <iostream>
using namespace std;

void changeValue(int x) {
    x = 50;  // تعديل النسخة فقط
}

int main() {
    int number = 10;
    changeValue(number);
    cout << "قيمة المتغير بعد الدالة: " << number << endl;
    return 0;
}

📤 النتيجة:

قيمة المتغير بعد الدالة: 10

رغم إنك عدّلت القيمة داخل الدالة، إلا أن القيمة الأصلية لم تتغير لأنك اشتغلت على “صورة طبق الأصل” من المتغير وليس عليه مباشرة.


🔹 مثال واقعي أكثر:

تخيل عندك برنامج تسجيل طلاب، وعندك دالة تحاول “تعديل عدد الطلاب”:

void enrollStudent(int totalStudents) {
    totalStudents += 1;
    cout << "داخل الدالة: " << totalStudents << endl;
}

int main() {
    int students = 25;
    enrollStudent(students);
    cout << "خارج الدالة: " << students << endl;
}

📤 النتيجة:

داخل الدالة: 26  
خارج الدالة: 25

🔸 السبب؟
التعديل تم على نسخة مؤقتة من المتغير، مش على الأصل.


⚙️ التمرير بالمرجع (Pass by Reference)

🔹 المفهوم:

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

يتم تعريف هذا النوع بإضافة & أمام اسم المعامل في الدالة.

🔹 مثال توضيحي:

#include <iostream>
using namespace std;

void changeValue(int &x) {
    x = 50; // تعديل مباشر على المتغير الأصلي
}

int main() {
    int number = 10;
    changeValue(number);
    cout << "قيمة المتغير بعد الدالة: " << number << endl;
    return 0;
}

📤 النتيجة:

قيمة المتغير بعد الدالة: 50

هنا الدالة ما تعاملت مع نسخة، بل عدّلت نفس المتغير في الذاكرة.


🔹 كيف تعمل في الذاكرة؟

C++ ببساطة بتقول:

“بدل ما أعمل نسخة جديدة، رح أستخدم نفس العنوان (Address) للمتغير.”

بالتالي أي تعديل داخل الدالة ينعكس فورًا على القيمة الأصلية.


🔍 مقارنة دقيقة بين النوعين

الخاصيةPass by ValuePass by Reference
هل يتم إنشاء نسخة جديدة؟✅ نعم❌ لا
الذاكرة المستهلكةأكثرأقل
السرعةأبطأ عند تمرير هياكل كبيرةأسرع
هل يتأثر المتغير الأصلي؟❌ لا✅ نعم
الأمانأكثر أمانًا (لن تُغيّر الأصل)خطر في حال تعديل غير مقصود
الاستخدام النموذجيدوال حسابية لا تُعدّل القيمدوال تُحدّث البيانات أو تُرجع أكثر من قيمة

🧠 التمرير بالمؤشرات (Pass by Pointer)

(أسلوب متقدّم، لكنه مرتبط مباشرة بالمرجع)

#include <iostream>
using namespace std;

void changeValue(int *ptr) {
    *ptr = 99;
}

int main() {
    int num = 10;
    changeValue(&num);
    cout << num;
}

📤 النتيجة:

99

في هذه الطريقة نمرر عنوان المتغير (&num) ونستخدم *ptr للوصول إلى القيمة الفعلية.
هذه الطريقة هي الأساس في التعامل مع الذاكرة الديناميكية (Dynamic Memory) وهياكل البيانات المتقدمة.


تجربة توضيحية مهمة

خلينا نجرب تمرير المصفوفة.

🎯 المثال:

#include <iostream>
using namespace std;

void updateArray(int arr[], int size) {
    arr[0] = 999;
}

int main() {
    int numbers[3] = {10, 20, 30};
    updateArray(numbers, 3);
    cout << numbers[0] << endl;
}

📤 النتيجة:

999

🔸 ليش؟
لأن المصفوفة يتم تمريرها بمرجع ضمنيًا — اسم المصفوفة هو مؤشر لأول عنصر فيها.


🔧 متى أستخدم كل نوع؟

الحالةالطريقة المناسبة
دالة حسابية مثل جمع أو طرحPass by Value
تعديل مباشر على متغير خارجيPass by Reference
التعامل مع مصفوفة أو كائن كبيرPass by Reference أو Pointer
تمرير قيم صغيرة ثابتةPass by Value
دالة تحتاج ترجع أكثر من قيمةPass by Reference

🧱 استخدام المراجع لإرجاع أكثر من نتيجة

#include <iostream>
using namespace std;

void calc(int a, int b, int &sum, int &diff) {
    sum = a + b;
    diff = a - b;
}

int main() {
    int x = 10, y = 4, s, d;
    calc(x, y, s, d);
    cout << "المجموع: " << s << " والفرق: " << d;
}

📤 النتيجة:

المجموع: 14 والفرق: 6

🧠 نقطة تقنية: ماذا يحدث فعليًا داخل الذاكرة؟

في Pass by Value:

  • يتم نسخ البيانات إلى مساحة جديدة في الذاكرة.
  • لذلك استدعاء الدوال يصبح أبطأ كلما كبرت البيانات (مثل المصفوفات الكبيرة).

في Pass by Reference:

  • يتم تمرير عنوان المتغير الأصلي فقط (عادة 4 أو 8 بايت).
  • لا يحدث نسخ، ولا يتم استهلاك ذاكرة إضافية.

⚠️ أخطاء شائعة يقع فيها المبرمجون

  1. ❌ تمرير متغير بالقيمة وتوقع أنه سيتغير: void edit(int x) { x = 100; } النتيجة؟ لن يتغير المتغير الأصلي.
  2. ❌ تعديل متغير عالمي عن طريق مرجع بدون قصد:
    يمكن أن يسبب تغييرات غير متوقعة في باقي الكود.
  3. ❌ نسيان إشارة المرجع & في تعريف الدالة:
    يجعلها تمر بالقيمة بدل المرجع دون قصد.
  4. ❌ تمرير كائن كبير بالقيمة:
    سيجعل البرنامج ينسخ كل محتوى الكائن — وهذا مكلف جدًا في الأداء.

🧭 الربط بالمفاهيم القادمة

هذا الدرس هو المدخل الأساسي لفهم:

  • المؤشرات (Pointers)
  • المراجع (References)
  • الكائنات في البرمجة الكينونية (OOP)
  • الدوال الصديقة (Friend Functions)
  • تمرير الكائنات إلى الدوال في C++

يعني باختصار: هذا الدرس هو “بوابة الذاكرة في C++”.


🌐 روابط خارجية موثوقة للتعمّق:


🧩 الخلاصة

  • التمرير بالقيمة = نسخة مؤقتة، آمنة، لكنها تستهلك ذاكرة أكثر.
  • التمرير بالمرجع = تعديل مباشر، أسرع، لكنه قد يسبب تغييرات غير متوقعة إن لم تُستخدم بحذر.
  • اختيارك للطريقة يعتمد على هدف الدالة وحجم البيانات ومدى حاجتك لتعديل الأصل.

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


اكتشاف المزيد من كود التطور

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

اترك رد

Scroll to Top