الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

مشكلة مع Delete Operator

بدأه Omar Eladel في 8 فبراير 2009 · 12 رد · 1,709 مشاهدة · في الأسئلة المجابة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكــم ورحمـة الله وبركاتــه

من أعظم مميزات لغة الـ ++C هى المؤشرات Pointers ، و من ضمن إستخداماتها هى Dynamic Memory Allocating ، و تعنى تحديد الذاكرة المستخدمة للبرنامج أثناء وقت التشغيل Run-Time و ليس وقت كتابة البرنامج و ترجمته Compile-Time .

أثناء مراجعتى على لغة الـ ++C بعد فترة غياب طويـــــــــــلة من أحد المواقع و الكتب و .... ، و عند وصولى إلى فصل المؤشرات Pointers و Dynamic Memory Allocating ،، من ضمن الدراسة ، يوجد delete operator ، و هو المستخدم فى تحرير الذاكرة بعد إستخدامها .

كتبت هذا الكود :

   #include <iostream>
   using namespace std;

   int main()
   {
	   int* a;
	   int n;
	   cout << "Enter n: ";
	   cin >> n;
	   a = new int[n];

	   cout << "Enter num: ";
	   for (int i=0; i<n; i++)
	   {
		   cin >> a;
	   }

	   for (int i=0; i<n; i++)
	   {
		   cout << "(1) You Entered " << a << endl;;
	   }

	   delete [] a;		 //Here is the problem
	   cout << "-------------Here is the problem-------------" << endl;
	   for (int i=0; i<n; i++)
	   {
		   cout << "(2) You Entered " << a << endl;
	   }
	   system("pause");
	   return 0;
   }

ناتج البرنامج كان كالتالى :

Enter n: 3

Enter num: 12 54 6

(1) You Entered 12

(1) You Entered 54

(1) You Entered 6

-------------Here is the problem-------------

(2) You Entered 0

(2) You Entered 54

(2) You Entered 6

هل هذا صحيح :wacko: اليس من المفترض ألا أستطيع الوصول إلى محتويات ما يشير إليه المؤشر a ؟؟

المشكلة ظهرت عند أستخدام delete operator ، و وظيفته هى تحرير الذاكرة المستخدمة من قبل المؤشر ،، لكنه قام بتحرير الذاكرة الخاصة بأول عنصر فقط ، عنوانه هو نفس قيمة المؤشر .

اليست وظيفته هى تحرير الذاكرة المستخدمة كلها التى تساوى عنوان المؤشر + قيمة المتغير n ؟

هل ما افهمه هذا صحيح ام خطأ ؟؟

راجع هذا الرابط (يحتوى على نقاش مشابه للمشكلة) : هنا

وفقكم الله ،،

#2

أسمع وصهلل معايا :D

new تقوم بحجز الذاكرة من الـprocess heap , اذا لم تكن تعرف ما هو الـprocess heap فيمكنك اعتبارة memory pool (كتلة ذاكرة يعني) كبيرة نقوم بتجزئتها حسب حاجتنا المختلفة للذاكرة .

كيف تكون عملية التجزئة ؟

هذه العملية تعتمد على الـheap manager وهو جزء من مهام النظام يعني لا دخل لّلغة نفسها في هذه العملية , المهم ان هناك اكثر من طريقة يمكن ان يستخدمها الـheap manager لعملية التجزئة منها على سبيك المثال استخدام الـPOOL_DESCRIPTOR , بحيث يتم ربط جميع الـPOOL_DESCRIPTORS عن طريق linkedlist .

كل POOL_DESCRIPTOR يحتوي على بيانات عن جزء معين من الـpool الكبير (الحجم/الـtag/مستخدم ام لا .. الخ) , هذه الصورة ربما توضح أكثر :

post-101943-1234059736_thumb.jpg

عند عملية الـallocation الذي يحدث هو ان الـheap manager يقوم بجرد الـpool_descriptors و يرى هل هناء جزء حجمه ملائم وحالته free , اذا لم يجد يقوم باخذ مسار اخر لا وقت للحديث عنه هنا , أصلا نحن نتكلم بشكل نظري .

عند عملية الـdeallocation سيقوم الـheap manager بتعليم الـجزء كـfree بدلا من busy , هذا الأمر لا يعني ان الذاكرة تم تحريرها فعليا ولا يمكن الوصول اليها , سيحتوي هذا الجزء على اي garbage (سيظل يحتوي على اخر بيانات تم حفظها فيه) الى ان يتم حجزه مرة اخرى في عملية allocation مختلفة .

تحرير الذاكرة عمليا يتم في حالات اخرى لكن الحديث عنها لاحقاً :wink:

mov eax, dword ptr ds:[0xffdf0308]

jmp dword ptr [eax+0xfc]

#3

شكراً أخى GM على الشرح

لكن انت قلت اثناء عملية التحرير أن الـ status => free (الحالة تحول إلى free) ، يعنى نظرياً ، اول عنصر من المصفوفة سيظل محتوياً على نفس قيمته طبقا لـ :

"GM" كتب:
عند عملية الـdeallocation سيقوم الـheap manager بتعليم الـجزء كـfree بدلا من busy , هذا الأمر لا يعني ان الذاكرة تم تحريرها فعليا ولا يمكن الوصول اليها , سيحتوي هذا الجزء على اي garbage (سيظل يحتوي على اخر بيانات تم حفظها فيه) الى ان يتم حجزه مرة اخرى في عملية allocation مختلفة .

صح أم خطأ ؟

لكن من التجربة ، تم مسح قيمة أول عنصر و تحويلها إلى 0 ، و أما بقية العناصر ، فكما هى .

نقطة أخرى ::

لو راجعت الرابط فى اخر الموضوع : هنا ، ستجد ان إستخدام delete بدون الأقواس [] يؤدى إلى مسح أول عنصر فقط ، اما إستخدامها مع الأقواس [] يؤدى الى تحرير جميع العناصر فى المصفوفة المحجوزة بإسم المؤشر .

هل هذا صحيح أم تهييس الليل :D ؟؟

تم تعديل هذه المشاركة بواسطة Omar Eladel في 8 فبراير 2009 في 05:42

#4

بعد حذف الذاكرة غير المؤشر الي قيمة خاليه:

  1.  
  2. delete [] a;
  3. a=0;
  4.  

هنا ستبتعد عن المشاكل على ما اعتقد.

tvquran_6.gif

#5
اقتباس
لكن من التجربة ، تم مسح قيمة أول عنصر و تحويلها إلى 0 ، و أما بقية العناصر ، فكما هى .

ماتدقش على دي :D , احنا بنتكلم نظري , عمليا يوجد headers توضع وتمسح اثناء الحجز و التحرير , يمكن التأكد عن طريق الـdebugging .

mov eax, dword ptr ds:[0xffdf0308]

jmp dword ptr [eax+0xfc]

#6
اقتباس
هل هذا صحيح أم تهييس الليل :D ؟؟

تهييس الليل .. :lol:

post-82612-1234064467_thumb.jpg

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#7

صحيح , هي الفائدة فقط مع الـObjects Array -> ضمان استدعاء الـdestructor لكل object في الـarray .

لكن ان تكلمنا عن تحرير الذاكرة فلا فرق ظهر لي الى الآن .

mov eax, dword ptr ds:[0xffdf0308]

jmp dword ptr [eax+0xfc]

#8

@Time1

الحمد لله إن النتائج اللى ظهرت لى مش كده ، لأن لو دى اللى ظهرت ، كان زمان الشاشة فى ..... :D

-------

مع الأخ time1 ، ظهرت نتائج عجيبة ، كيف حدث هذا ؟ الذاكرة تم تحريرها ، فكيف امتلأت بقيم Garbage كهذه ؟؟

بعد كل هذا و اللخبطة ، ما الفائدة من delete operator (بقوسين و من غير قوسين) ؟

#9

السلام عليكم ,,

اقتباس
بعد كل هذا و اللخبطة ، ما الفائدة من delete operator (بقوسين و من غير قوسين) ؟

أخ عمر, GM في الحقيقة أعطاك ما يحصل فعلياً تحت windows ...

و لكن لنتحدث عن عملية الـ initialization و الفرق بين delete و []delete ....

عندما تحجز أي قطعة من الذاكرة سواء عن طريق الـ dynamic memory أو عن طريق التالي مثلاً :

int x;

أو

int x[10];

فإن عملية الـ Initialization هي من اختصاص المبرمج و ليس من اختصاص المترجم أو حتى معامل حجز الذاكرة, و ذلك أمر بديهي لأن new مثلاً لا تعرف مالذي يريده المبرمج :)

القول بأن الذاكرة يجب أن تصفر هذا كلام خاطئ تماماً, ماذا لو كان لدينا كائن. في حالة الكائنات فإن new تستدعي الـ constructor و delete تستدعي الـ destructor .. كما ترى عملية الـ initialization هي من اختصاص المبرمج سواء في حالة الكائنات أو في حالة المتغيرات البسيطة..

كما ترى فإن معنى أن الذاكرة حجزت لا يعني أنها صفرت أو أصبحت قيمة تلك القطعة تساوي قيمة ما, و إنما معناها أن "برنامجنا" أصبح يملك تلك القطعة من الذاكرة.

هناك في الحقيقة غموض عن الحديث عن ملكية الذاكرة, ما معنى أن برنامجنا يملك تلك القطعة من الذاكرة ؟

لابد من تحديد نظام التشغيل, الذي تبرمج له, و عندها يمكنك تعريف معنى الملكية :)

أخي GM عرض لك الموضوع تحت windows :)

و بالمثل فإن عملية تحرير الذاكرة, هي عملية التخلي عن ملكية تلك القطعة من الذاكرة.

لاحظ أنك لا تحتاج كمبرمج معرفة أي من التفاصيل مادمت تقوم بامتلاك الذاكرة عن طريق new و التخلي عنها عن طريق delete.

الآن لماذا يوجد معاملان لحجز الذاكرة و معاملان لتحرير الذاكرة ؟

كل معامل حجز الذاكرة مع معامل التحرير الخاص به :)

int* x = new int;
delete x;

int* x = new int[size];
delete[] x;

قبل أن أقول لك الفرق بين تلك المعاملات, لابد أن ضغطك أصبح مليون من كثر ما أقول معامل معامل معامل معامل...

الحقيقة أن new و delete بنكهاتها المختلفة ليست إلا معاملات يمكن "إعادة تعريفها"

يمكن أن تقوم بتغيير تصرف new و delete حسب رغبتك.

و هو في الحقيقة السبب لماذا يوجد نوعين من المعاملات.

في حالة حجز المصفوفات ربما تريد حفظ عدد العناصر في مكان آخر أو حجز int إضافة قبل المصفوفة يحتوي على عدد العناصر. و أدع الباقي لخيالك :)

تحياتي ,,

#10

أخى خالد ،، كفيت و وفيت ،،

لكن ما أود الإستفسار عنه هو :

Khaled.Alshaya كتب:

كما ترى فإن معنى أن الذاكرة حجزت لا يعني أنها صفرت أو أصبحت قيمة تلك القطعة تساوي قيمة ما, و إنما معناها أن "برنامجنا" أصبح يملك تلك القطعة من الذاكرة.

هناك في الحقيقة غموض عن الحديث عن ملكية الذاكرة, ما معنى أن برنامجنا يملك تلك القطعة من الذاكرة ؟

لابد من تحديد نظام التشغيل, الذي تبرمج له, و عندها يمكنك تعريف معنى الملكية :)

هنا قلت ، أن حجز الذاكرة يعنى أن برنامجنا يمتلك هذه الذاكرة .... يعنى فى هذه القطعة من الكود ....

a = new int[n];

	   cout << "Enter num: ";
	   for (int i=0; i<n; i++)
	   {
		   cin >> a;
	   }

	   for (int i=0; i<n; i++)
	   {
		   cout << "(1) You Entered " << a << endl;;
	   }

..... تعنى أن برنامجنا يمتلك عدد من المتغيرات = n

حجم هذه المتغيرات = sizeof(int) * n

مضبوط ؟؟

لكن بعد هذا السطر :

delete [] a;

نحن تخلينا عن عدد المتغيرات = n

و الحجم = sizeof(int) * n لم نعد نمتلكه

الان نمتلك فقط المؤشر a و عنوانه &a و من الممكن ان نستخدمه مرة أخرى فى حجز عدد من المتغيرات ديناميكياً

مضبوط ؟؟؟

لكن نحن تخلينا عن المتغيرات التى قام هذا المؤشر بحجزها لذلك لا يحق لنا إعادة أستخدامها

مضبوط ؟؟

لكن ظهر عكس هذا فى هذا الجزء :

for (int i=0; i<n; i++)
	   {
		   cout << "(2) You Entered " << a << endl;
	   }

هنا أستخدمنا المتغيرات التى حجزها المؤشر a ، و هذا غير صحيح نظرياً لأن تحريرنا للذاكرة يعنى تخلى برنامجنا عنها كما ذكرت أنت ...

كيف هذا ؟؟

حتى الان أنا مستوعب الموضوع نظرياً ،، لكن لماذا لا يحدث هذا داخل أروقة المترجم ؟

وفقكم الله ،،

----------

تعديل ::

بالنسبة لنقطة Initialization أخى خالد ، و كما ذكرت أنت انها من أختصاص المبرمج ، و أنا كنت اعتقد ان تحرير الذاكرة يعنى تحويلها إلى 0 ، فهذا خطأ منى بدون قصد ، فمع كم اللخبطة هذه ، وجدت انى نسيت كل ما قرأت عن الـ ++C من قبل ..

وفقكم الله ،،

تم تعديل هذه المشاركة بواسطة Omar Eladel في 8 فبراير 2009 في 13:59

#11

تحرير الذاكره لا يعني مسح القيمه في ذلك المكان ، فقط جعل هذا المكان قابل للأستخدام من قبل أي برنامج أخر .. وقد يصدف أن لا تتغير هذه القيمه ومن خلال العنوان الذي لديك (المؤشر) تستطيع الوصول لتلك المنطقه ، وقد يصدف أن تتغير كما في مثال أخي time .

بالتوفيق :) .

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#12

من المعروف ان الحاسب لا يقوم بحذف الذاكره وانما يجعلها متاحه

هذا حاله حال الهارديسك حيث بامكانك استرداد بياناتك الممسوحه كيف؟

ما تم هو ان البيانات الممسوحه تم وضع امكانية الاستخدام بالفهرسه - امكانية الكتابه -.

وهذا يتم بالذاكره المستخدمه هنا فبعد الحذف والمفروض ان نسميه تحرير وليس حذف

من الممكن ان نجد برنامج اخر استخدم هذه الجزء فنجده قد تغير ومن الممكن العكس.

هذا مفهومي فالموضوع كله يدور حول الفهرسه .

متغيب

#13

الحمد لله فهمت الموضوع ،

انا كنت اعتقد انه عندما يتم تحرير الذاكرة الديناميكية بإستخدام المعامل delete ، يتم مسح القيمة التى يحملها المؤشر الذى كان يشير الى المصفوفة ، لذلك اعتقدت انى لن استطيع الوصول الى محتويات ما يشير اليه المؤشر ، لذلك كنت متعجباً من قدرة البرنامج على الولوج الى قيمة المتغيرات ، لكن الحقيقة انه يتم تحويل حالة المكان فى الذاكرة الى free ، اى من الممكن حجزها مرة اخرى ، مع الاحتفاظ بالعنوان الذى يشير اليه المؤشر .

اشكر كل اخوتى و اساتذتى الذين شاركوا فى الموضوع و فى المناقشات ،،

وفقكم الله ،

مواضيع مشابهة