مضى على الشبكة و يوم من العطاء.

[ شرح ] لغة C وتخصيص الذاكرة (Memory Allocation) الشرح الأول

ما شاء الله 🔥
الله يجزاك خير على هذا الشرح وينفع بك وبعلمك يارب… بانتظار القادم
شكرا جدا على كلامك, كلك زوق واخلاق
الشروح القادمة اقوى إن شاء الله 🔥
الله يبارك فيك وفي اهلك ويحميكم جميعا ويطول بعمركم إن شاء الله 🌹
 
  • Love
التفاعلات: BAYAN
بسم الله والحمد لله والصلاة والسلام على رسولنا محمد وعلى اله وصحبه اجمعين
اللهم علمنا ما ينفعنا وإنفعنا بم علمتنا إنك انت العليم الحكيم
السلام عليكم عائلتي الكريمة, اخواني واخواتي الكريمين والكرام جميعا, حياكم الله

آسف للتأخر عليكم في نشر الشرح ولاكن كان لدي ظروف فإتطررت للتأخر عليكم, بتمنى من الجميع يسامحني ولنبدأ على بركة الله
في هذا الشرح, سوف نتحدث عن مفهوم تخصيص الذاكرة (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, كيف يمكنني إستخدامها؟
دالة 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");
رابعا, نستخدم دالة memset لتصفير قيم جميع العناصر في المصفوفة كالآتي
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");
وبعد ان تقومو بتنفيذ الكود سوف نرى بأن قيم المصفوفة في نفس المسار في الذاكرة الديناميكية قد تم تغييرهم بنجاح

وهيك بيكون خلص شرح اليوم
بتمنى تسامحوني على الشكل الرديء يلي عرضتلكم المنشور فيه, اقصد الألوان وإلخ...

كنت اريد ان اشرح لكم المزيد ولاكن لا أريد الإطالة عليكم, إذا اردتم فممكن اكمل الشرح في المنشور القادم إن شاء الله
اتمنى الإفادة والإستفادة للجميع, بارك الله فيكم جميعا
دمتم بسلام, تحياتي للجميع والسلام عليكم ورحمة الله وبركاته 🌹 ❤️
ماشاء الله تبارك الله
دائما تبهرنا بأعمالك الرائعة
بارك الله فيك ونفع بك
استمر أخي
 
ماشاء الله تبارك الله
دائما تبهرنا بأعمالك الرائعة
بارك الله فيك ونفع بك
استمر أخي
يخليلي اياكم جميعا, والله عم نطبق يلي تعلمناه منكم ومن مهاراتكم, لا أكثر
الله يحفظكم ويحميكم انتو وعيالكم ويبعد عنكم الحرام ويرفع مكانتكم ومقامكم في الجنة وعند الله إن شاء الله ❤️ 🌹
 

آخر المشاركات

فانوس

رمضان
عودة
أعلى