التخصيص الديناميكي للهياكل
جزء من قسم Logic & Flow في رحلة C على Coddy — الدرس 48 من 63.
حتى الآن، تعلمت كيفية إنشاء الـ structs على الـ stack - فهي توجد ضمن النطاق (scope) الذي تم التصريح عنها فيه ويتم تنظيفها تلقائياً عند انتهاء ذلك النطاق. ولكن ماذا لو كنت بحاجة إلى بقاء الـ struct إلى ما بعد نطاقه الأصلي، أو عندما لا تعرف في وقت التجميع (compile time) عدد الـ structs التي ستحتاج إليها؟
هنا يأتي دور التخصيص الديناميكي. يمكنك استخدام malloc() لإنشاء هياكل (structs) في الذاكرة الكومة (heap)، تماماً كما تفعل مع أي نوع بيانات آخر. الفرق الجوهري هو أن الهياكل المخصصة ديناميكياً تبقى في الذاكرة حتى تقوم بتحريرها صراحةً باستخدام free().
إليك الصيغة البرمجية لتخصيص struct ديناميكيًا:
struct Point *ptr = malloc(sizeof(struct Point));يقوم هذا بتخصيص ذاكرة كافية في الـ heap لاستيعاب هيكل Point واحد ويعيد مؤشراً إلى تلك الذاكرة. يمكنك بعد ذلك استخدام عامل السهم (arrow operator) للوصول إلى أعضاء الهيكل وتعديلها:
ptr->x = 10;
ptr->y = 20;تذكر دائماً التحقق مما إذا كان malloc() قد نجح (أعاد قيمة non-NULL) واستدعاء free(ptr) عند الانتهاء من استخدام الـ struct لمنع تسرب الذاكرة. هذه التقنية ضرورية لبناء برامج مرنة يمكنها إنشاء وإدارة هياكل البيانات في وقت التشغيل (runtime).
التحدي
سهلقم بإنشاء برنامج بلغة C يوضح التخصيص الديناميكي للهياكل (structs) باستخدام malloc(). يجب أن يقوم برنامجك بما يلي:
- تعريف
structباسمCarبالأعضاء التالية:- عدد صحيح
yearلتخزين سنة التصنيع - مصفوفة أحرف
brandبحجم 20 لتخزين علامة السيارة التجارية - مصفوفة أحرف
modelبحجم 25 لتخزين طراز السيارة - رقم عشري
priceلتخزين سعر السيارة - عدد صحيح
mileageلتخزين المسافة المقطوعة للسيارة
- عدد صحيح
- في الدالة الرئيسية (main function)، قم بالتصريح عن مؤشر لهيكل
CarباسمcarPtr - استخدم
malloc()لتخصيص ذاكرة ديناميكيًا لهيكلCarواحد وتعيين العنوان المرتجع إلىcarPtr - تحقق مما إذا كان تخصيص الذاكرة ناجحًا:
- إذا كان
carPtrيساويNULL، اطبعMemory allocation failedواخرج من البرنامج - إذا نجح التخصيص، اطبع
Memory allocation successful
- إذا كان
- اقرأ قيم الإدخال التالية وقم بتعيينها لأعضاء الهيكل باستخدام عامل السهم (arrow operator):
- اقرأ عددًا صحيحًا للسنة وقم بتعيينه باستخدام
carPtr->year - اقرأ سلسلة نصية للعلامة التجارية وقم بتعيينها باستخدام
carPtr->brand - اقرأ سلسلة نصية للطراز وقم بتعيينها باستخدام
carPtr->model - اقرأ رقمًا عشريًا للسعر وقم بتعيينه باستخدام
carPtr->price - اقرأ عددًا صحيحًا للمسافة المقطوعة وقم بتعيينه باستخدام
carPtr->mileage
- اقرأ عددًا صحيحًا للسنة وقم بتعيينه باستخدام
- بعد قراءة جميع القيم، قم بإجراء الحسابات التالية باستخدام عامل السهم:
- احسب عمر السيارة بطرح السنة من 2024:
int age = 2024 - carPtr->year; - احسب معدل الاستهلاك بناءً على العمر: إذا كان العمر أكبر من 10، اضبط الاستهلاك على 0.6، وإلا فاضبطه على 0.8
- احسب القيمة الحالية:
carPtr->price * depreciation
- احسب عمر السيارة بطرح السنة من 2024:
- اطبع معلومات السيارة باستخدام عامل السهم بهذا التنسيق الدقيق:
Car Information:Year: [year]Brand: [brand]Model: [model]Original Price: [price]Mileage: [mileage]Age: [age] yearsCurrent Value: [current_value]
- قم بتحرير الذاكرة المخصصة ديناميكيًا باستخدام
free(carPtr) - اطبع
Memory freed successfully
يختبر هذا التحدي فهمك للتخصيص الديناميكي للهياكل باستخدام malloc()، والتحقق من فشل التخصيص، والوصول إلى أعضاء الهيكل من خلال المؤشرات باستخدام عامل السهم، وتحرير الذاكرة المخصصة بشكل صحيح باستخدام free(). ستتدرب على دورة الحياة الكاملة لإدارة الذاكرة الديناميكية مع الهياكل.
ورقة مرجعية
استخدم malloc() لتخصيص الهياكل (structs) ديناميكيًا على الكومة (heap):
struct Point *ptr = malloc(sizeof(struct Point));الوصول إلى أعضاء الهيكل (struct) من خلال المؤشرات باستخدام عامل السهم (arrow operator):
ptr->x = 10;
ptr->y = 20;تحقق دائمًا مما إذا كان malloc() قد نجح وقم بتحرير الذاكرة عند الانتهاء:
if (ptr == NULL) {
// معالجة فشل التخصيص
}
// استخدم الهيكل...
free(ptr);جرّب بنفسك
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// TODO: هنا Car قم بتعريف هيكل
int main() {
// TODO: carPtr باسم Car قم بالإعلان عن مؤشر لهيكل
// TODO: لتخصيص ذاكرة لهيكل واحد malloc() استخدم
// TODO: تحقق مما إذا كان تخصيص الذاكرة ناجحاً
// قراءة قيم المدخلات
int year, mileage;
char brand[20], model[25];
float price;
scanf("%d", &year);
scanf("%s", brand);
scanf("%s", model);
scanf("%f", &price);
scanf("%d", &mileage);
// TODO: قم بتعيين قيم المدخلات لأعضاء الهيكل باستخدام عامل السهم
// TODO: احسب العمر، الاستهلاك، والقيمة الحالية
// TODO: اطبع معلومات السيارة بالتنسيق المطلوب
// TODO: قم بتحرير الذاكرة المخصصة واطبع رسالة نجاح
return 0;
}يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Logic & Flow
1أساسيات المؤشرات
ما هو المؤشر؟الإعلان عن المؤشراتعامل العنوان (&)عامل إلغاء المرجعية (*)مؤشرات NULLمراجعة: أساسيات المؤشرات4مشروع: أداة نصوص بسيطة
نظرة عامة على المشروععد الأحرف2المؤشرات والمصفوفات
أسماء المصفوفات كمؤشراتعناصر المصفوفة - المؤشراتالعمليات الحسابية على المؤشراتمقارنة المؤشراتمراجعة: التنقل في المصفوفة باستخدام المؤشرات5المؤشرات والدوال
التمرير بالقيمةتمرير المؤشرات إلى الدوالتعديل المتغيرات عبر المؤشراتمثال كلاسيكي: التبديلتمرير المصفوفات إلى الدوالملخص: وسائط المؤشرات في الدوال8الهياكل والمؤشرات
المؤشرات إلى الهياكلعامل السهم (->)تمرير الهياكل بالقيمةتمرير مؤشرات الهياكلالتخصيص الديناميكي للهياكلمراجعة: تعديل الهياكل باستخدام المؤشرات11تحديات المراجعة النهائية
مراجعة: دمج السلاسل النصية الديناميكيمراجعة: مصفوفة من الـ Structsمراجعة: عداد تكرار الكلمات3مصفوفات الأحرف والسلاسل النصية
السلاسل النصية كمصفوفات charالمُنهي الصفري (Null Terminator)إدخال السلاسل النصية باستخدام scanfاستخدام strlen()استخدام strcpy()استخدام strcat()استخدام strcmp()مراجعة: دوال السلاسل النصية الأساسية