C++: تخصيص الذاكرة الديناميكي

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

في الدروس السابقة، كانت المتغيرات والمصفوفات تُخصص على المكدس — ثابتة الحجم، تُحرر تلقائيًا عند انتهاء الدالة.

لكن إذا كنت تحتاج تخزين كمية غير محددة من البيانات (مثلًا، المستخدم يدخل 1000 عدد صحيح، لكنك لا تعرف وقت الترجمة)، فأنت تحتاج تخصيص الذاكرة الديناميكي — تخصيص ذاكرة على الكومة.



1. المكدس مقابل الكومة

(1) 1.1 منطقتا الذاكرة

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

(2) 1.2 تشبيه من الحياة

تشبيه من الحياة المقابل البرمجي
مطعم الوجبات السريعة (قائمة ثابتة، الطعام جاهز فور الطلب) المكدس
بوفيه مفتوح (خذ بقدر ما تريد) الكومة


2. new و delete

(1) 2.1 تخصيص الذاكرة بـ new

الصياغة:

▶ مثال 2: إدارة الذاكرة الديناميكية (صعوبة ⭐)

TEXT 📖 للعرض فقط
Type* pointerName = new Type;

الخرج:

TEXT 📖 للعرض فقط
(Program output)

مثال:

CPP
#include <iostream>

int main() {
 // Allocate an int on the heap
 int* p = new int; // p Pointing to int on the heap
 
 *p = 5; // Assign value to int on the heap
 std::cout << "*p = " << *p << std::endl; // 5
 
 return 0;
}

💡 نقطة مهمة: new تُرجع مؤشرًا — يشير إلى الذاكرة المخصصة على الكومة.


(2) 2.2 تحرير الذاكرة بـ delete

الصياغة:

TEXT 📖 للعرض فقط
delete pointerName;

مثال:

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

int main() {
 int* p = new int;
 *p = 5;
 
 std::cout << "*p = " << *p << std::endl;
 
 delete p; // ✅ Free heap memory
 p = nullptr; // ✅ Good practice:Set to nullptr after freeing
 
 return 0;
}

💡 القاعدة الذهبية: new و delete يجب أن يأتيا معًا دائمًا — كل new يحتاج delete.


(3) 2.3 نسيان delete يسبب تسرب الذاكرة

مثال خطأ:

CPP
#include <iostream>

void foo() {
 int* p = new int;
 *p = 5;
 // ❌ Forgot to write delete p;
} // Function ends, p is destroyed, but int on heap is not freed (memory leak)

int main() {
 for (int i = 0; i < 1000000; i++) {
 foo(); // Each call leaks 4 bytes
 }
 // Program memory usage keeps growing, may eventually crash
 return 0;
}

💡 تسرب الذاكرة: ذاكرة مخصصة على الكومة لم تُحرر أبدًا ولم يعد يمكن الوصول إليها (لأن المؤشر إليها دُمر).



3. المصفوفات الديناميكية

(1) 3.1 تخصيص مصفوفات بـ new

الصياغة:

TEXT 📖 للعرض فقط
Type* pointerName = new Type[size];

مثال:

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

int main() {
 int n;
 std::cout << "Please enter the array size: ";
 std::cin >> n;
 
 // Allocate an array of n ints on the heap
 int* arr = new int[n];
 
 // Using the array
 for (int i = 0; i < n; i++) {
 arr[i] = i + 1;
 }
 
 // Output the array
 for (int i = 0; i < n; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 // Free array(Note:Use delete[],not delete)
 delete[] arr;
 
 return 0;
}

💡 نقطة مهمة: لتحرير مصفوفة، استخدم delete[] (بأقواس مربعة)، وليس delete!


(2) 3.2 new و delete يجب أن يتطابقا

التخصيص التحرير
new int delete p;
new int[10] delete[] p;

مثال خطأ:

CPP
int* p = new int[10];
delete p; // ❌ Error: Should use delete[] p;


4. المؤشرات المعلقة

(1) 4.1 ما هو المؤشر المعلق؟

المؤشر المعلق هو مؤشر يشير إلى ذاكرة مُحررة بالفعل.

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

int main() {
 int* p = new int(5);
 delete p; // Free memory
 
 // ❌ Dangerous: p is now a dangling pointer
 std::cout << *p << std::endl; // Undefined behavior! May output garbage or crash
 
 return 0;
}

💡 الحل: اضبط المؤشر على nullptr فورًا بعد التحرير:

CPP
delete p;
p = nullptr; // ✅ Good practice


5. تطبيق: فرز مصفوفة ديناميكية

▶ مثال 1: فرز الاختيار مع مصفوفة ديناميكية (صعوبة ⭐⭐)

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

// Selection Sort
void selectionSort(int* arr, int n) {
 for (int i = 0; i < n - 1; i++) {
 int minIdx = i;
 for (int j = i + 1; j < n; j++) {
 if (arr[j] < arr[minIdx]) {
 minIdx = j;
 }
 }
 // Swap
 int temp = arr[i];
 arr[i] = arr[minIdx];
 arr[minIdx] = temp;
 }
}

int main() {
 int n;
 std::cout << "Please enter the array size: ";
 std::cin >> n;
 
 int* arr = new int[n]; // Dynamically allocate array
 
 std::cout << "Please enter " << n << " Integer: " << std::endl;
 for (int i = 0; i < n; i++) {
 std::cin >> arr[i];
 }
 
 selectionSort(arr, n); // Sort
 
 std::cout << "After sorting: " << std::endl;
 for (int i = 0; i < n; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 delete[] arr; // Free memory
 arr = nullptr;
 
 return 0;
}

الخرج:

TEXT 📖 للعرض فقط
Please enter the array size: 
Please enter  Integer: 
After sorting: 
 

نتيجة التشغيل:

TEXT 📖 للعرض فقط
Please enter the array size: 5
Please enter 5 Integer: 
5 3 1 4 2
After sorting: 
1 2 3 4 5


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

(1) 6.1 الحذف المزدوج

مثال خطأ:

CPP
int* p = new int(5);
delete p;
delete p; // ❌ Error: Double free of same memory (undefined behavior)

الإصلاح: اضبط على nullptr فورًا بعد التحرير (delete nullptr آمن).

TEXT 📖 للعرض فقط
delete p;
p = nullptr;

(2) 6.2 نسيان delete

مثال خطأ:

TEXT 📖 للعرض فقط
int* arr = new int[10];
// ❌ Forgot to write delete[] arr;

العاقبة: تسرب الذاكرة.


❓ أسئلة شائعة

س لماذا أستخدم الذاكرة الديناميكية؟ ألا يمكنني استخدام المصفوفات فقط؟
ج إذا كنت تعرف الحجم وقت الترجمة، المصفوفات كافية. لكن إذا: > - الحجم يحدده إدخال المستخدم > - الحجم قد يكون كبيرًا جدًا (يتجاوز سعة المكدس) > - تحتاج البيانات للبقاء بعد انتهاء الدالة

▶ مثال 3: مصفوفة ديناميكية (صعوبة ⭐)

CPP
#include <iostream>

int main() {
    int size = 5;
    int* arr = new int[size];

    for (int i = 0; i < size; i++) {
        arr[i] = i * 10;
    }

    for (int i = 0; i < size; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;

    delete[] arr;

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

الخرج:

TEXT 📖 للعرض فقط
0 10 20 30 40
💡 نصيحة: new int[n] تخصص مصفوفة، delete[] تحرر مصفوفة. لا تنسَ []!



📖 ملخص

📝 تمارين

  1. أساسي (صعوبة ⭐): استخدم new لتخصيص double، أسند له 3.14، أخرجه، ثم حرره.

  2. متوسط (صعوبة ⭐⭐): دع المستخدم يدخل n، استخدم new لتخصيص مصفوفة int بحجم n ديناميكيًا، أدخل n أعداد صحيحة، أوجد القيمة العظمى، ثم حرر الذاكرة.

  3. تحدي (صعوبة ⭐⭐⭐): نفّذ صنف "مصفوفة ديناميكية" باستخدام الذاكرة الديناميكية (ستتعلم الأصناف لاحقًا؛ استخدم struct الآن):

  4. يتضمن: int* data (مؤشر لمصفوفة الكومة)، int size (الحجم الحالي)، int capacity (السعة)

  5. نفّذ دالة push_back: إذا كان size == capacity، أعد تخصيص كتلة ذاكرة أكبر (new مصفوفة أكبر، انسخ البيانات، حرر القديمة)

  6. اختبر: أضف 100 عنصر


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

الآن بعد أن تعلمت تخصيص الذاكرة الديناميكي، لنتابع إلى المؤشرات والمراجع: تطبيق شامل (الدرس 27) — تعزيز كل ما تعلمته من خلال أمثلة متكاملة!

Web-Tutorial.com

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

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

100%