





السمعة:
- إنضم20 يونيو 2023
- المشاركات 274
- الحلول 5
- مستوى التفاعل 352
- النقاط 63
بسم الله والحمد لله والصلاة والسلام على رسولنا محمد وعلى آله وصحبه أجمعين
اللهم علمنا ما ينفعنا وإنفعنا بم علمتنا إنك انت العليم الحكيم
السلام عليكم عائلتي الكريمة, إخواني وأخواتي الكرام جميعًا, حياكم الله
اللهم علمنا ما ينفعنا وإنفعنا بم علمتنا إنك انت العليم الحكيم
السلام عليكم عائلتي الكريمة, إخواني وأخواتي الكرام جميعًا, حياكم الله
آسف للتأخر عليكم في نشر الشرح ولاكن كان لدي ظروف فإتطررت للتأخر عليكم, بتمنى من الجميع يسامحني ولنبدأ على بركة الله
في هذا الشرح, سوف نتحدث عن مفهوم تخصيص الذاكرة (Memory Allocation)
أولًا, ما هو تخصيص الذاكرة (Memory Allocation) ؟
كما يمكننا اأن نخمن من اسمه, تخصيص الذاكرة في لغة C هو حجز مكان في الرامات بمساحة معينة ومع عدد عناصر محدد وهنالك نوعين من تخصيص الذاكرة
طيب, ما هم هذان النوعان من تخصيص الذاكرة؟
هما:
- تخصيص الذاكرة الثابتة (Static Memory Allocation)
- تخصيص الذاكرة الديناميكية (Dynamic Memory Allocation)
طيب ماذا يعني تخصيص الذاكرة الثابتة؟
تخصيص الذاكرة الثابتة هو تخصيص مساحة من الذاكرة أثناء وقت الترجمة (compile time) من لغة عالية المستوى (High-Level Language) إلى لغة منخفضة المستوى (Low-Level Language) مثل binary (لغة الآلة), hexadecimal (نظام عد الستة عشري) او لغة اسامبلي
هذا يعني أن حجم الذاكرة المطلوبة يتم تحديده قبل تشغيل البرنامج وبالتالي لا يمكن تغيير حجم الذاكرة أثناء تشغيل البرنامج
طيب كيف ممكن أخصص مساحة, قسم أو جزء من الذاكرة كتخصيص ثابت ؟
بكل بساطة بقلك إنه هي العملية هي عبارة عن تعريف متغير كما سوف أفعل في الكود التالي:
C:
int staticVar;
في الكود الذي في الأعلى لقد تم حجز مساحة بالذاكرة لعنصر 1 بحجم int و staticVar هو اسم المتغير
بـتسألني وبتقول, طيب إذا بدنا نخزن أو نحجز 3 أماكن في الذاكرة وبحجم int بدي أكتب int int int staticVar; ؟؟
بقلك لا يانور عيوني, إذا بدك تحجز مكان في الذاكرة ل 3 عناصر وبحجم int وبدك إياهم يكونوا قريبين لبعض بمسار الذاكرة فبدك تسوي مصفوفة من نوع int وبتحتوي على 3 عناصر مثل ما مكتوب في الكود التالي:
C:
int staticVarArray[3];
وهيك بتكون حجزت مكان لثلاث عناصر والمسارات قريبة من بعضها وفي سطر كود واحد
بتقلي, طيب إذا بدي يكونوا بعيدين عن بعضهم كمسار في الذاكرة؟
بقلك سوي 3 متغيرات int وكل واحد منهم بيأخد منك سطر, فمجموع الأسطر لعمل هي العملية هو 3 أسطر وبالتالي فبتكون خسران بهالحركة إذا نظرت لك بعين محلل للخوارزميات والكود سوف يكون كالتالي:
C:
int staticVar1;
int staticVar2;
int staticVar3;
وهيك بنكون خلصنا من شرح تخصيص الذاكرة الثابتة
إذا ما هو تخصيص الذاكرة الديناميكية وما تفاصيله ؟
لا تستعجل يا أخي و أختي الكريم والكريمة, سوف أتوقع الأسئلة التي تخطر على بالكم وسوف أكتبها وأجيب عنها في هذا المنشور ولكن تحلى بالصبر, إنما للشياطين مكانه في أعمال العجلة أبعده الله عنا وعنكم وعن أمة المسلمين جميعًا
تخصيص الذاكرة الديناميكية هو تخصيص لمساحة الذاكرة أثناء وقت التشغيل (runtime), هذا يعني أن حجم الذاكرة يمكن أن يتغير أثناء تشغيل البرنامج
طيب كيف ممكن أخصص مساحة, قسم أو جزء من الذاكرة كتخصيص ديناميكي ؟ سوف أقول لك أن هناك طريقتين لتنفيذ هي العملية
ماهم هاتان الطريقتان, هما دالتين;
- malloc (Memory Allocate)
- calloc (Contiguous Allocate)
قبل أي شيء وأول شيء يجب علينا فعله هو إضافة المكتبة التي تدعم هذه الدوال أو الدالات التي نحتاج لاستخدامها في هذا الدرس والتي سوف نشرحها:
C:
#include<stdlib.h>
نقوم بإضافة الكود أعلاه إلى جانب المكاتب الملف أو إلى أعلى الكود
طيب ماهو ال syntax الخاص بهاتين الدالتين وهل يأخذان Parameters (باراميترات) أو مدخلات ؟
نعم, كلامك صحيح يا عزيزي القاريء, هناك مدخلات لكلا الدول منهم, لنتحدث عنهم بالترتيب

دالة malloc هي دالة تستخدم لحجز او تخصيص مساحة ديناميكية, متغيرة في الذاكرة وبالتالي سيكون بإمكانك تغيير القيم أثناء عمل البرنامج بدون أية مشاكل والsyntax الخاص بالدالة malloc كالآتي:
C:
malloc(حجم البيانات);
وبالنسبة لأحجام البيانات فلا تقلق يا عزيزي القاريء, فأنا افتكرت ذلك و وضعته بالحسبان, هذا هو الجدول بكل البيانات التي سوف تحتاجها خلال مسيرتك في هذه العملية:
نوع البيانات (Data Type) | المدى او النطاق (Range) | الحجم بال بيت (Size in (Bytes)) |
bool | false او true | 1 |
unsigned char | 0 إلى 255 | 1 |
signed char او char | -128 إلى +127 | 1 |
wchar_t | 0 إلى 65,535 | 2 |
unsigned short int او unsigned short | 0 to 65,535 | 2 |
signed short int او short int او signed short او short | -32,768 to 32,767 | 2 |
unsigned int | 0 to 4,294,967,295 | 4 |
signed int او int | -2,147,483,648 to 2,147,483,647 | 4 |
unsigned long int او unsigned long | 0 to 4,294,967,295 | 4 |
signed long int او long int او signed long او long | -2,147,483,648 to 2,147,483,647 | 4 |
unsigned long long int او unsigned long long | 0 to 18,446,744,073,709,551,615 | 8 |
signed long long int او signed long long او long long | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | 8 |
float | 3.4E-38 to 3.4E+38 (7 خانات) | 4 |
long float | 3.4E-38 to 3.4E+38 (7 خانات) | 4 |
double | 1.7E-308 to 1.7E+308 (15 خانة) | 8 |
long double | 1.7E-308 to 1.7E+308 (15 خانة) | 8 |
طيب هل إذا بدي أكتب حجم الـ int بكتب 4 وهل مجبر أو يتوجب علي أن أحفظ أحجام أنواع البيانات؟
أسئلة جيدة يا عزيزي القاريء, السؤالين مرتبطين ببعضهم البعض لذا سأجيب عليهم بجواب واحد
نعم يمكنك كتابة 4 لحجم int ولكن في المجال هي الطريقة بدائية شوي وغير محببة وإن كنت تريد بأن تختصر على نفسك الوقت بالإضافة إلى ان يكون عملك شغل محترفين, فنصيحة مني استخدم الطريقة التالية فأنت محظوظ لأنك لن تكون مجبر على حفظ كل هذه المعلومات لأن الطريقة التالية تختصر عليك طريق ووقت ياعزيزي القاريء ولن تندم إن شاء الله
الحل هو استخدام دالة اسمها sizeof وهي الدالة له باراميتر أو مدخل واحد وهو متغير أو نوع البيانات, عندما تستدعي هذه الدالة وتعطيها ما تطلب فإنها تعطيك ما تريد, لنرى المثالين التاليين على استخدامها مع دالة malloc
C:
malloc(sizeof(نوع البيانات));
C:
malloc(sizeof(المتغير));
إذا نجحت عملية تخصيص المساحة فسوف ترجع الدالة مسار أو مؤشر أول عنصر من العناصر التي تم تخزينها في المصفوفة وفي الذاكرة أما إذا فشلت العملية فسوف يتم إرجاع قيمة NULL
بدك تقلي ممكن مثالين بالطريقتين, بقلك تكرم:
C:
malloc(sizeof(short));
C:
short sh3ll_users_count = 2056;
malloc(sizeof(sh3ll_users_count));
ونتيجة الكودين اللذان في الأعلى هي متساوية أو متطابقة بالأصح
بتقلي بدي أخزن القيمة في متغير, بقلك ممكن وتكرم عيونك ولكن لازم يكون متغير ومؤشر بنفس الوقت مثل ما في الكود التالي:
C:
int* ptr = (int*) malloc(sizeof(int));
أو يمكنك القيام بنفس العملية وتخزين القيمة بدون عمل تحويل لنوع البيانات (Casting)
C:
int* ptr = malloc(sizeof(int));
بتسألني وبتقول, طيب ليش حاط إشارة عملية الضرب والتي هي نجمة (*) بجانب int أو نوع البيانات, بقلك لأنه هي النجمة (*) عندما يتم وضعها بجانب نوع البيانات أو int فهي تحول نوع البيانات إلى نوع البيانات نفسه ولكن على إنه مؤشر وبالتالي هو مسار لمتغير أو قيمة في الذاكرة.
بتقلي ثانية ثانية, كيف, شلون خزنت مصفوفة بداخل متغير int من نوع مؤشر ؟
بقلك أنت قلتها, مؤشر وبالتالي المؤشر هو عبارة عن مسار ولا يعرف سوى إنه يمكن أن يحتوي على أكثر من قيمة في نفس المتغير ويمكن التعديل على حجم المتغير في الذاكرة لاحقا لذلك قرروا بأن تكون دائمًا نوع بياناته مصفوفة وبالتالي أصبحت تعرف يا عزيزي القاريء بأنك لا تحتاج لوضع اقواس مربعة بجانب اسم المتغير لجعله مصفوفة لأن المؤشر هو مصفوفة بالفعل.
ملاحظة: الطرق والاكواد يلي نفذناها بالأعلى تقوم بتخصيص ذاكرة ديناميكية لعنصر واحد فقط
شلون بدي أخصص ذاكرة ديناميكية لأكثر من عنصر ؟
الطريقة سهلة يا عزيزي القاريء, ما عليكم سوى ضرب حجم نوع البيانات بعدد العناصر والsyntax هو كالتالي:
C:
malloc(عدد العناصر * sizeof(نوع البيانات));
بدك مثال, تكرم:
C:
malloc(5*sizeof(long));
أما العدد 10 هو عدد العناصر وبالتالي ستكون قيمة المتغير هي مسار أول عنصر في الذاكرة وداخل المصفوفة
بتقلي طيب بدي اطبع القيمة الحقيقية للمتغير وليس مساره, بقلك تكرم:
C:
printf("%d ", ptr[0]);
قمنا بطباعة أول عنصر موجود في المصفوفة التي تم تخزينها في الذاكرة, وبالتالي سيتم طباعة رقم سلبي أو قيمة سلبية لأننا لم ندخل أي قيمة للعنصر رقم 0 في المصفوفة, خلينا ندخل قيمة للعنصر الأول في المصفوفة:
C:
ptr[0] = 2;
وهيك بنكون خلينا قيمة العنصر الأول في المصفوفة هي 2, خلينا نطبع قيمة العنصر الأول ونجرب ونشوف مع بعض:
C:
printf("%d ", ptr[0]);
بعد التجريب سوف ترون بأن النتيجة هي 2 وهذا يعني أن أمورنا ماشية بالخير, أما إذا حاولنا نتخطى عدد العناصر يلي ضربناه بحجم نوع البيانات بداخل الدالة malloc فسوف تكون القيمة 0 اثناء محاولتنا لطباعته.
C:
printf("%d ", ptr[11]);
بعد التجريب سوف ترون بأن النتيجة هي 0, طب بتقلي بدي أخلي القيم بعيدة عن بعضها, بقلك بدك تعمل مثل ماعملنا في تخصيص الذاكرة الثابتة, بدك تسوي متغيرات من نوع مؤشر منعزلة عن بعضها.
طيب ماهي دالة calloc, كيف يمكنني استخدامها؟
دالة calloc هي توأم الدالة malloc

نصيحة مني هيك أعتبرها لأنها تأدي نفس وظيفة الدالة malloc ولكن تنفذ أمر إضافي في النهاية وهذا الذي يميذها عن malloc,
ما الفرق الذي يميز calloc عن malloc وما هو الsyntax الخاص بالدالة calloc
خلينا نشوف الفرق يلي بينهم مع بعض
شوف عزيزي القاريء, قلتلك أنه عند محاولة قرائة قيمة لعنصر من المصفوفة قبل إدخال قيمته فإنه يعطيك قيم سلبية أو أعداد عشوائية وليس لها أي مصدر
ولكن هذا الشيء لا يحصل مع دالة calloc لأنه يقوم بتغير جميع قيم العناصر إلى 0 وبالتالي هذه العملية التي تميز calloc عن malloc
أما بالنسبة للsyntax الخاص بدالة calloc فهو كالآتي:
C:
calloc(حجم العنصر, عدد العناصر);
لكي لا أطيل عليكم, أريد أن أقول لكم بأن كل شيئ ذكرناه حول دالة malloc يطبق أيضًا على دالة calloc بإستثناء الميزة التي تفرق بينهما
أريد أن أزيدك من الشعر بيت وأقلك يا عزيزي القاريء إنه من ممكن أن تصفر العناصر اأثناء استخدام دالة malloc, أقصد يمكنك تأدية نفس عملية calloc ولكن بدون الحاجة إليها, كيف؟ باستخدام دالة تدعى memset
ولكن لا يمكنك استخدامها بدون استدعاء المكتبة التي تحتويها, أليس كذلك؟ صحيح, هناك مكتبتين أعرفهم يدعمون الدالة التي نحتاجها وهم المكاتب التالية:
1-مكتبة string.h
C:
#include<string.h>
2-مكتبة memory.h
C:
#include<memory.h>
أنا شخصيًا استخدم مكتبة memory لأنني أراها مكتبة لها صلة بالكود منطقيًا, أما الخيار فأتركه لك عزيزي القاريء
ماهي دالة memset, ماذا تفعل, ماهو الsyntax الخاص بها او ماهي مدخلاتها او البراميترات الخاصة بها؟
دالة memset هي دالة تقوم بعملية تغيير قيم لكمية من العناصر المتواجدة في مصفوفة
دالة memset لها ثلاث باراميترات أو مدخلات والsyntax الخاص بها هو كالتالي:
C:
memset(حجم العناصر, قيمة العناصر, (نقطة البداية في المصفوفة) او المصفوفة نفسها)
لتحقيق هدفنا وهو تأدية وظيفة الدالة calloc ولكن بدون استخدامها, أقصد فقط باستخدام الدالة malloc و memset
أولًا, نقوم بتعريف متغير يحمل عدد عناصر المصفوفة والذي سيتم إستخدامه في دالة malloc
C:
int sh3ll_users_count = 10;
ثانيًا, نقوم بتعريف متغير يحمل قيمة الدالة malloc والذي يفترض بأن يكون مؤشر أو Pointer
C:
int* mPtr = (int*) malloc(sh3ll_users_count * sizeof(sh3ll_users_count));
ثالثًا, نقوم بطباعة القيم المتواجة في المصفوفة للإثبات والتأكد من صحة العملية
C:
printf("قبل تنفيذ دالة memset: \n");
for(int i = 0; i < sh3ll_users_count; i++) {
printf("%d ", mPtr[i]);
}
printf("\n");
C:
memset(mPtr, 0, sh3ll_users_count * sizeof(sh3ll_users_count));
C:
printf("بعد تنفيذ دالة memset: \n");
for(int i = 0; i < sh3ll_users_count; i++) {
printf("%d ", mPtr[i]);
}
printf("\n");
وبعد أن تقوموا بتنفيذ الكود سوف نرى بأن قيم المصفوفة في نفس المسار في الذاكرة الديناميكية قد تم تغييرهم بنجاح
وهيك بيكون خلص شرح اليوم, بتمنى تسامحوني على الشكل الرديء يلي عرضتلكم المنشور فيه, أقصد الألوان وإلخ...
كنت أريد أن أشرح لكم المزيد ولكن لا أريد الإطالة عليكم, إذا أردتم فممكن أكمل الشرح في المنشور القادم إن شاء الله
أتمنى الإفادة والإستفادة للجميع, بارك الله فيكم جميعًا
دمتم بسلام, تحياتي للجميع والسلام عليكم ورحمة الله وبركاته
دمتم بسلام, تحياتي للجميع والسلام عليكم ورحمة الله وبركاته


التعديل الأخير بواسطة المشرف: