الشبكة العربية لمطوري الألعاب

خبير  أحمد عزالدين مشاركة 1

السلام عليكم

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

اولا : قبل الدخول في صلب الموضوع - دعنا نجرب الاتى :-
اذا كان لدينا برنامج يحجز ذاكرة ديناميكية اثناء عمله فكلنا نعلم انه يجب تحرير
هذه الذاكرة بعد الانتهاء منها - انظر الكود التالى

void DoSomeCalculation()
{
// here i may create a dynamic array using dynamic memory faciliites
// provided in c/c++ like

int arrSize = 0;
// get size from user or any other way

// create the array based on the new size 
int* arr = new int[arrSize];

// here we may use the created array for some work

// before returnning from this functions
// we must delete and free up the created dynamic memory
delete[] arr;
}


اذا قام احدنا وفكر اننا عرفنا المتغير arr في الدالة السابقة علي انه local
وبالتالى لم يقم بتحرير الذاكرة التي انشأها
(وهو يعتقد ان هذه الذاكرة انشأت محليا والمفروض ان تحذف بعد الخروج من الدالة ولكن هذا غير صحيح)
فان ذلك سيسبب تسريب في الذاكرة memory leak
لانه فعلا عندما تنتهى الدالة من تنفيذها يقوم البرنامج بحذف قيم كل المتغيرات التي عرفت
محليا او داخليا في الدالة local ولان المؤشر ايضا عبارة عن متغير فان قيمته ستحذف
ولكن قيمته تمثل لنا عنوان الذاكرة الديناميكية التي انشأناها واذا تم حذف هذا العنوان
فلن نعرف ابدا مكان هذه الذاكرة التي تم انشاءها في الدالة وبذلك يحدث تسريب.

ثانيا : كيف نعرف انه حدث تسريب للذاكرة نتيجة نسيان تحرير الذاكرة الديناميكية التى انشأناها
هناك مجموعة من الاسباب مذكورة في هذا المقال
http://www.codeguru.com/forum/showthread.php?t=312742
وانا لما بحثت عن بعض الادوات التى قد تساعد فى ايجاد اخطاء او تسريب فى الذاكرة
وجدت ان ابسط طريقة هى الاتى :-
// to use debug information, just added these lines to your program
// note: you should add them as is, do not change thier order

#define _CRTDBG_MAP_ALLOC
#include 
#include 

// after adding these,
// you can easily add this line as a first line in your program

// to enable debugging info in the output windows
// when this app close for any reason, normally or not
// this function will check this mem. app for leaks and such things
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF |                                

_CRTDBG_LEAK_CHECK_DF );
/*
 something to know, we passed these flags to the previous function
 to allow tracking of memory even when your program have the probaility
 to exit at many diffrenet places
*/


بالمناسبة الكود السابق يعرض لنا معلومات اضافية في نافذة ال debug output
تساعدنا علي العنوان الذي سبب المشكلة ومعلومات اخرى.

هذا كل ما اعرفه عن موضوع تسريب الذاكرة وارجو من الاخوة التعليق ان كان لديهم
ما يريدون التعليق عليه.
واتمنى لو يقوم احد الاساتذة بشرح المعلومات الموجودة في نافذة ال debug output
في حالة حدوث اى تسريب - وكيف نستفيد من هذه المعلومات.

ثالثا : اذا كان لدينا متغير من نوع مؤشر مثل IDirect3DDevice9* ونريد ان نمرر هذا عنوان
هذا المؤشر الي دالة لتقوم بالتعديل على محتوياته ونحن بهذا نريد تمرير عنوان المتغير
فالطرق المتاحة لاستقبال هذا العنوان فقط طريقتين هما
1- pointer to pointer notation
2- pointer to reference notation
انظر الكود الاتى لتفهم اكثر
// let say we have a pointer defined like this
IDirect3DDevice9* device = 0;
// and we need to pass this pointer address to some functions that
// will create this device for us (this just example to provide the idea)
/*
 NOTE: even if these functions that we will call them and pass them
              our variable (which is pointer) just need the device to be in this
              form IDirect3DDevice9*, we also must use the method provided here
 NOTE: C++ alwayes pass parameter by copying thier value.
*/

// we may define the function here like that
void function(IDirect3DDevice9** d)
{
    // work with 'd' here and create it or even do whatever you wish
}
// or like that
void function2(IDirect3DDevice9* &d)
{
    // work with 'd' here and create it or even do whatever you wish
}

/*
when we wanna pass the variable 'device'
we just call the function like that
function(&device);
or
function(device);
*/

ارجو ان تكون قد وصلت الفكرة
وهذا ينطبق علي اى نوع من انواع المتغيرات

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

أحمد عزالدين
طالب دراسات عليا
جامعة كالجري

خبير  سعيد بسيوني مشاركة 2

في Jan 30, 2008 00:55، عقد ahmed ezz حاجبيه بتفكير وقال:

ثالثا : اذا كان لدينا متغير من نوع مؤشر مثل IDirect3DDevice9* ونريد ان نمرر هذا عنوان
هذا المؤشر الي دالة لتقوم بالتعديل على محتوياته ونحن بهذا نريد تمرير عنوان المتغير
فالطرق المتاحة لاستقبال هذا العنوان فقط طريقتين هما
1- pointer to pointer notation
2- pointer to reference notation


هزا الكلام صحيح، هزا هو أسلوب الدايركت إكس إس دي كيه في إعادة القيم.
بس أن لية وجهة نظر برضو يعني لو بتبص معاية كده:
بشكل عام يضطر المبرمج لاستخدام هزا الأسلوب في حال ان القيمة التي يعيدها الإجراء محجوزة لمعلومة ما (مثلا قيمة boolean تحدد إزا مشي حال الإجراء ولا لأ). في هزه الحال الطريقة النضيفة الوحيدة اللي ممكن تستخدمها هي جعل صاحب النداء يمرر القيم الخارجة كبارامترات تمثل عناوين متغيرات يتم تعبئتها عند الخروج من الإجراء.
تمرير العنوان أساسي هنا لأنك عاوز تغير في قيمة حاجة مش معلن عنها في الـ local scope.

يعني لو كان الإجراء بتاعك بيرجعش حاجة (return type is void) فأنا شخصياً بفضل اني أرجع حاجة زي IDirect3DDevice* بدال ما استقبل عنوانها وأغيرها.

خبير مدير وسام البهنسي مشاركة 3

عرفت بعض الأشخاص الذين يخافون من رؤية مؤشر مزدوج (عنوان مؤشر) مثل:
IDirect3DDevice9** ppDevice

لذلك يمكنك أيضاً لتبسيط الأمور مع أي object من DirectX استخدام التعريفات التي تغفل المؤشر إلى الـ interface، مما يجعل الأمور أسهل للقراءة وأخف وطأة على قلوب الناس. كمثال:
PDIRECT3DDEVICE9* ppDevice
هذا المتغير مكافئ تماماً للمتغير الذي ذكرته في البداية، لكن في هذا السطر نستخدم فقط نجمة واحدة (*) للتعبير عن أننا نأخذ في هذا المتغير عنوان device object.
بالمثل يمكنك استخدام التعريفات لكل الـ interfaces الأخرى في DirectX مثل:

PDIRECT3DSURFACE9
PDIRECT3DTEXTURE9
PDIRECTDRAWSURFACE
PDIRECTSOUNDBUFFER

وهلم جراً... باستخدام هذه التعريفات، يصبح مثلاً لدينا الفرق الآتي:
bool LoadLevel(const char* levelName, IDirect3DDevice9** outputSurface);
تصبح
bool LoadLevel(const char* levelName, PDIRECT3DSURFACE9* outputSurface);

وسام البهنسي
مبرمج في إنفيديا وإنفريمز