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

المؤشرات مالها وماعليها

رائج
بدأه HGB في 8 نوفمبر 2003 · 28 رد · 20,600 مشاهدة · في المواضيع والدروس
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ...

ستكون هذه سلسلة دروس باذن الله للتعرف بشكل أفضل على الؤشرات ... وازالة الغموض الكامن حولها .. باذن الله ...

بداية هذه مقدمة عن المؤشرات ... المؤشرات يمكن أن توصف بثلاث كلمات ...

مميزة و مرنة وخطيرة في ان واحد...

مقدمة:

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

فنجد ان الفيجوال بيزك لا تعطي المبرمج حرية أبدا كمبرمج السي بلس بلس ... نظرا لخطورة التعامل مع المؤشرات وادارة الذاكرة يدويا ... وأيضا لغة الجافا التي اشتقت من السي بلس بلس لاتعطي المبرمج الحرية المطلوبة ....

وللغة الجافا ململم نفايات أو Garbage Collection ويمكن الاختصار له ب GC مهتم بالغاء تخصيص كميات الذاكرة المحجوزة يدويا بشكل الي ... ومن الملاحظ أن أكثر أدوات لغة الجافا مبيعا تلك المهتمة باكتشاف الاخطاء وخصوصا (بطء عمل التطبيقات ) بسبب ال GC .... فال GC سيتسبب بعمليات تنظيف دورية للذاكرة مما يتسبب ببطء البرنامج .

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

فبتكامل المؤشرات مع الوراثة مع الفئات مع الوراثة وتعدد الاشكال نصل لمراحل متقدمة جدا ي هندسة البرنامج ككل وسنتطرق لذلك لاحقا ...

أما ثالثا الخطورة .. خطورة التعامل المباشر مع الذاكرة له سيئاته طبعا بالنسبة لعدم المتمرسين والحذرين ...

نعلم أنه يجب الغاء تخصيص أي ذاكرة يتم حجزها يدويا بالغاء يدوي أيضا ... والا سيتم توقف البرنامج أو انتهاك ذاكرة النظام انتهاكا سيئا يضر بالنظام ككل ...

وغالبا ماينسى المبرمج تحرير الذاكرة التي انشئها ... واذا لم ينسى فقد يحرر الذاكرة في وقت غير مناسب يمكن أ يسبب في شلل البرنامج ككل....

مما يؤدي الى أصعب أنواع الاخطاء على الاطلاق الا وهي أخطاء ال استثناءات وانتهاكات الذاكرة .(وسنتناول بتفصيل أكثر في الدروس عن هذه النقطة) ....

يوجد نوع اخر من الاخطاء المنطقية التي لاتتسبب في وقوف البرنامج انما حدوث نتائج غير مرغوبة وهي أيضا معفدة وصعبة الاكتشاف....

ولهذا قامت مايكروسوفت أخيرا بانتاج طقم الدوت نت لأسباب كثيرة منها مشكلة التعامل مع الذاكرة بهذه الطريقة المباشرة خصوصا لمبرمجي السي بلس بلس ...

حيث أدخلت مايكروسوفت GC خاص بمنصة الدوت نت لكن يتم تفعيله في السي بلس بلس دوت نت بشكل إختياري ... فلم يتم الغاء الحجز اليدوي في نسخة السي بلس بلس الخاصة بمنصة الدوت نت والتي تدعى ب++ Manged c أو (شيفرة سي بلس بلس المدارة) .... فكل ماحدث ان زادت مرونة السي بلس بلس في نواح جديدة ولكن للأسف تم الغاء يعض ميزات المرونة الاخرى لكي تتدنى من كل من لغتي ال #C و ال

Visual Basic.Net ... قد ندخل في موضوع ال ++Manged C لاحقا.

فأقول أن مشكال الادارة اليدوية للذاكرة أو التعامل على المستوى الادنى مع المؤشرات قد وجدت صدى جيدا في ماكروسوفت ورأت أن تنتهج منهج صانعي لغة الجافا وتدير الذاكرة بشكل الي بسبب أن محاسن الادارة اليدوية أقل من المساويء ... طبعا للمبتدئين ... فأدخلت ال GC ليتم الاختيار بين التحرير اليدوي للذاكرة أو التحرير الالي أو كليهما معا وهذا طبع بالنسبة للمؤشرات....

والمؤشرات هي ليست الا متغيرات تحمل قيم عناوين في الذاكرة وليست قيما حقيقية كالارقام والحروف وماشابه ذلك ....

مثلا :

int * q ;

مؤشر ونحجز له ذاكرة قبل ان نتعامل معه على النحو التالي ...

q = new int ;

ف ال q حاليا تحمل قيمة لعنةان في الذاكرة .....

اما اذا كتبنا

int w ;

فيكون قد حجز لنا الحاسوب بشكل الي مكانا قي الذاكرة لمتغير واحد صحيح وال

w تحمل قيمة لعدد صحيح وليس مؤشرا ...

وسنفهم الفرق بشكل مفصل في الجلسات القادمة ....

نسخة منظمة ومنسقة في ملف PDF لكامل هذا الموضوع قامت بتنسيقه الأخت dr_sara جزاها الله خيرا

Pointers.pdf

تم تعديل هذه المشاركة بواسطة HGB في 26 ديسمبر 2007 في 14:45 — السبب: إضافة ملف PDF لكامل الموضوع

banner_60_468.gif

NOTHING IS IMPOSSIBLE

#2

ننتظر بفارق الصبر الدروس

HGB

كذلك ودنا الدروس تكون مدعومة بأمثلة قوية

في لغة C

يعطيك العافية اخوي وشكرا على المقدمة الحلوة

#3

السلام عليكم

مشكور على المجهود الطيب

الله يعطيك العافية

وإلى المزيد ..

تحياتي

#4

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

ستكون هناك أمثلة باذن الله لكن بعد أن نفهم الأساسيات ....

<span style='font-size:14pt;line-height:100%'>الجلسة الأولى :

سنحاول في هذه الجلسة أن نفهم الفرق بين

1)

المتغيرات التي تحمل قيما كالأعداد والحروف وبين المتغيرات التي تحمل عناوين في الذاكرة

2) ثم اذا كان لدينا متغير يحمل قيمة كيف يمكننا أن نعرف عنوانه في الذاكرة

واذا كان لدينا متغير يحمل عنوان كيف يمكننا أن نحصل على القيمة التي في ذلك العنوان

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

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

الان فالنذهب الى النقطة الاولى ...

الفرق بين المتغيرات التي تحمل قيما وتلك التي تحمل عناوين .. في الصيغة الكتابية تكون على النحو التالي ...

المتغيرات العادية ...

; int y

المؤشرات ...

 ; int * x

الفرق كما هو واضح فقط في علامة * التي تعني أن هذا المتغير x سيحمل عنوانا وليس قيمة ...

الى هنا سنذهب الى النقطة الثانية ونتعرف على باقي الفروق لاحقا ...

اذا كان لدينا متغير يحمل قيمة كيف يمكننا أن نعرف عنوانه في الذاكرة ؟؟

مثال 1 :

; int y =10

من الملاحظ أن قيمة y هنا هي 10 ... ولكن ماهو عنوان هذا المتغير في الذاكرة ؟

لمعرفة ذلك وطباعة قيمة العنوان على الشاشة ...

; cout << " y value is : "<<y
; cout << "address is :" << & y

لاحظ لل & هنا تعني أوجد عنوان الشيء الذي يلي الحرف & يعني هنا أوجد عنوان y ..

سيقوم الجهاز بطاعة قيمة العنوان المحجوز لل y ... وستكون النتيجة قيمة ستعشرية .. وقد تكون على الشكل التالي ....

y value is :10
address is : 0x8f51fff3  ...

لكن لن تكون القيمة السابقة ثابته لكل جهاز فحتما ستختلف من جهاز لأخر لأن البرنامج يقوم بحجز القيم في أماكن تتعلق بالنظام ككل ...

أما بالنسبة للمتعيرات التي تحمل عناوين " المؤشرات " .....

واذا كان لدينا متغير يحمل عنوان كيف يمكننا أن نحصل على القيمة التي في ذلك العنوان ؟؟

الامر كالتالي ....

مثال 2 :

; int * x

لن نمهد ل x بقيمة حاليا وانما لاحقا سنعرف كيف نمهد للمتغيرات ...

; x = 50*
; cout << " x value is : "<< *x
; cout << " x address is : "<< x
x value is  10
x address is 0x8f51fff3 ......

لاحظ هنا أولا أننا أعطينا x قيمة 50 لكن x عنوان وليس قيمة لذا فاننا وضعنا العامل * قبل المتغير x لنرمز لى اننا نريد قيمة المتغير الذي يلي العامل * ... وهنا يعني نريد قيمة x لنعين لها المقدار 50 ....

لاحظ في النتائج على الشاشة أن خرج القيمة سيكون50 وهي القيمة لل x* والعنوان هو x فقط ...

وهذا عكس مايحدث للمتغيرات المعرفة بأنها عادية وتحمل قيما ...

عندها ملخص النتائج ...

العامل & يستخدم لاستخراج عنوان من متغير في الاصل يحمل قيمة

والعامل * يستخدم لأستخراج القيمة من متغير هو في الاصل يحمل عنوانا ....

أخيرا هناك مفاجأة !! المثال 2 لن يعمل ... وسيعطيك المصرف بأن هنا انتهاك للذاكرة ...

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

لكن فلنعلم انه عند انشاء متغير عادي يحمل قيمة يقوم الكمبيوتر بشكل الي بحجز مكان في الذاكرة لهذا المتغير الجديد ....لهذا السبب المثال الأول سيعمل بشكل جيد لأنه عندما عيينا القيمة 10 للمتغير y كان المتغير y له مكان صالح في الذاكرة ...

أما المثال الثاني فعندما عينا القيمة 50 لتوضع في المتغير x * كقيمة ... لم نكن قد أنشئنا مكانا في الذاكرة للمتغير x بعد ... لأن الامر

; int * x لايفعل شيئا ولايحجز مكانا صالحا في الذاكرة للمتغير x .. وانما فقط يخبر المصرف أن هناك متغير اسمه x يمكن أن يحمل عنوانا ... أما عملية الحجز فيتركها المصرف لك كليا ولك الحرية في ذلك ... لكن تذكر لاتعين قيمة لمتغير مؤشر قبل أن تحجز له مكانا في الذاكرة ... ... وحجز الذاكرة سيكون يدويا كما ذكرت ونستخدم لذلك أمرا أساسيا جديدا وهو new وسصبح لونه بالازرق في مصرف لغة السي ليشير الى أنه كلمة اساسية ...

والصيغة كالتالي....

; int * x
; x = new int

وهكذا اذا أكملنا المثال 2 ستعرض النتائج كما هو موضح في الخرج ...

لأننا الان حجزنا مكانا صالحا في الذاكرة للمتغير x .....

وأخيرا يجب أن نعرف أننا يجب أن نعين متغير عنوان الى عنوان ومتغير قيمة الى قيمة ....

ولايمكن ان نعين قيمة الى عنوان أو عنوان الى قيمة ... يعني بلغة الامثلة ...

مثال 3:

; int * x
; x = new int 
; int y
 ; x = y    // ERROR x is address  and y is Value 
 x= y // Good y is Value and *x is value *
x = & y // Good x is address and &y is address

والان وبعد أن عرفنا هذه المعلومات ... يأتي السؤال المهم ولماذا نريد أن نعرف عنوان متغير في الذاكرة .... ؟

لن أجيب الان وانما في الجلسة القادمة ولكن يجب لكي تفتح ذهنك أن تعرف أن المؤشرات هي المصفوفات ... كيف ؟ سنعرق في الجلسة القادمة...

تم تعديل هذه المشاركة بواسطة فيصل الحربي في 21 مايو 2011 في 09:07

banner_60_468.gif

NOTHING IS IMPOSSIBLE

#5

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

الجلسة الثانية :

عرفنا في الجلسة السابقة كيف يمكننا انشاء مؤشرات وكيف نحجز لها مكان في الذاكرة وكيف نعين قيما لتخزن في داخل تلك العناوين ,,,,

الان يجب أن نعلم أنه يمكننا حجز أكثر من مكان في الذاكرة ,,,, فمثلا في المثال ,,,

مثال 1:

; in t * x
; x = new int

حجزنا موقعا واحد فقط في الذاكرة لهذا المتغير x ...

في المتغيرات العادية يمكننا أن نحجز متغير زاحد أو أكثر باستخدام المصفوفات ,,,

مثال 2:

; int z[10]         // Array

المتغير z يمكنه تخزين 20 قيمة من النوع الصحيح ,,,,

كيف يمكن أن نفعل ذلك في المؤشرات ....

مثال 3:

; int * x
; x = new int [10]   // Our Pointer

هنا حجزنا 10 خانات للمتغير x وعملية الحجز هذه تحجز الاماكن في الذاكرة بحيث تكون متتالية في قيم العناوين بفرق 4 بايت بين كل عنوان والاخر " 4 بايت هو حجم ال int " ....... والمفاجيء أن طريقة استعمال المصفوفات والمؤشرات متشابهه تماما وأقصد متشابهه بالنسبة لطريقة تعين المتغيرات لكل منهما كالتالي ,,,,

يالنسبة للمثال 2 الذي يستخدم المصفوفات سنمرر القيمة 10 للثلاثة عناصر الأولى ,,,

مثال 4:

;z[0] = 10 
 ;z[1] = 10 
 ;z[2] = 10

وبالنسبة للمثال 3 الذي يستخدم المؤشرات ....

مثال 5:

; x[0] =10
; x[1] =10
; x[2] =10

المفاجيء أن لهما نفس طريقة الاستعمال !! والمفاجيء أكثر أن المصفوفات هي المؤشرات تماما ... لكن بفرقين بسيطين سنذكرهما بعد قليل ,,,,

قبل أن نعرف الفرقين يجب أن نعرف أننا يمكن أن نكتب المثالين السابقين بطريقة أخرى وهي منطقية تماما لمن فهم الجلسة الاولى ....

مثال 6:

10 = (z+0)* 
10 = (z+1)* 
10 = (z+2)*

وبالنسبة للمؤشرات ...

مثال 7:

10 = (x+0)* 
10 = (x+1)* 
10 = (x+2)*

مالعلاقة بين المثالين السابقين وبين الذين قبلهما ؟؟

التدوين[ z[2والتدوين (z+2)* متشابهين تماما كما هو ملاحظ ... لكن كيف حدث ذلك ؟

قلنا سابقا أن العامل * الغرض منه هو استخراج القيمة التي في العنوان ...

يعني x* تعني القيمة التي في العنوان x ...." وهي تساوي (x+0)* "

وقد قلنا سابقا أن العناوين المحجوزة بالمؤشرات أو المصفوفات تحجز على التوالي بفرق عدد بايتات من نوع المصفوفة أو المؤشر ....

حسنا , هاقد استخرجنا القيمة الموجودة في العنوان الاول x ... لكن كيف نستخرج باقي القيم التي في باقي خانات المؤشر ال 10 التي حجزناها في المثال 3 ؟؟

الطريقة سهلة ,, وهي بأن نقول استخرج القيمة التي في العنوان التالي للعنوان x ..كالتالي ...

(x+1)* حيث أن العنوان الذي يلي العنوان x هو x+1 باستخدام العامل * نستخرج القيمة التي في هذا العنوان " لكن لاحظ يجب استخدام الاقواس " ....

القيمة التي في العنوان الذي يليه (x+2)* وهكذا ....

يمكنك أن تستخدم هذا النوع من الصيغ أو الصيغة الاساسية للمصفوفات ...

لكنك في بعض الاماكن الضيقة ستفضل تدوين ال * .....

الان من أوجه الشه أيضا بين المؤشرات والمصفوفات طريقة استعمالهما للتمرير الى الدالات ...

لنفرض ان لدينا الدالة التالية .....

مثال 8 :

int Function( int W[] )       // Our Function
}
            ; [cout << W [0 
            ; [cout << W [4
            ; [cout << W [6
{

والتي نمرر لها مصفوفة فتقوم بطباعة العنصر الاول والرابع والسادس من المصفوفة ....

اذا كانت مصفوفتنا عادية كما في المثال 2 سنمررها كالتالي ...

مثال 9 :

  ; ( Function ( z

ف المتغير z هنا هو عنوان أول عنصر في المصفوفة z ...

واذا كنا نستخدم مصفوفات التي تعمل بالمؤشرات كما في المثال 3 ....

مثال 10 :

 ; ( Function ( x

لاحظ لافرق بين النوعين اطلاقا .... فأريد أن أقول أن المؤشرات والمصفوفات متشايهتان قي أمور كثيرة لكن يمكن أن نميز الفرق بينهما بالثلاثة فروق التالية...

1)عنوان مصفوفة المؤشرات ديناميكي ....أما عنوان المصفوفة العادية ستاتيكي"ثابت"

2) حجم مصفوفة المؤشرات ديناميكي ولايشترط معرفته قبل وقت التشغيل ... أما حجم المصفوفات العادية فهو ثابت ويجب معرفته قبل وقت التشغيل ...

3) يمكن الغاء الذاكرة الخاصة بمصفوفات المؤشرات في أي وقت من البرنامج ,, واذا نسيت الغاءها فلن يلغيها لك البرنامج الا بعد انتهاءه وتوقفه عن العمل ...أما الذاكرة الخاصة بالمصفوفات العادية فلا يمكنك الغاءها متى تشاء فالغائها يتم بشكل الي بعد الخروج من مدى الرؤية الخاص بها " SCOPE"....للثلاثة أسباب الفائتة اخترعت المؤشرات لتزيد من مرونة البرمجة بشكل فعال جدا ....

والخروج من ضيق المصفوفات الى سعة المصفوفات ... ويمكنكمنالان أن تلغي جميع استخداماتك للمصفوفات العادية وتستعمل بدلا منها تدوين المؤشرات ....

.............................

الان سنشرح الفرق الأول ...

عنوان مصفوفة المؤشرات ديناميكي ....أما عنوان المصفوفة العادية ستاتيكي"ثابت" ..

لنأخذ المثال التالي الذي سيوضح تماما المقصود ...

مثال 11 :

  ;int *x
  ;[ x= new int [10
;[ int z [20

الان يمكننا كتابة أن ..

 ; x = z

ولايمكننا كتاية أن

; z = x

لأن عنوان ال z سيكون ثابتا طوال فترة تشغيل البرنامج .. وسعطى لل z فقط وقت انشاء المتغير " أي وقت التعريف "

أما ال متغير x فعنوانه يعطى وقت التعريف لكن يمكن تغييره في أي لحظة ...

كما في المثال أعلاه قمنا بتعيين عنوان جديد لل x كما في

; x = z

فلن يعترض المصرف لأنه يمكنك تغيير العنوان كما تشاء لأنك تتعامل مع مؤشر ...

وأخيرا قبل أن ننهي هذه الجلسة مالسبب الذي يدعوك لتغيير عنوان المصفوفة ؟؟

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

لكن التدوين ..

; x= z

يغنيك عن كل هذا ... فالذي حدث هنا أن المصفوفة x أصبحت محتويات عناصرها هي نفسها التي في المصفوفة z ..... وبكل سهولة ...

فالذي حدث هنا أن عنوان المصفوفة x أصبح هو نفسه عنوان المصفوفة z ...

لذا فان عنوان أول عنصر لل x سيكون هو نفسه في ال z والثاني هو الثاني هناك والثالث كذلك .. لكل عناصر المصفوفة ...

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

وسنشرح الفرقين الباقيين بين المؤشرات والمصفوفات ... لكي نغوص بعدها في الجلسات التي تليها أكثر في عالم المؤشرات ...

تم تعديل هذه المشاركة بواسطة فيصل الحربي في 21 مايو 2011 في 09:15 — السبب: تعديل حاوية الاكواد

banner_60_468.gif

NOTHING IS IMPOSSIBLE

#6

شرح رائع

مشكووور جزيل الشكر

واااصل .. نحن معك

تحياتي ,,

#7

بارك الله فيك و جزاك الله كل خير لقد أفدتني كثيراً

أتمنى منك أن تبقى على اهتمام بموضوع لأنه بالفعل لديك أسلوب مميز...

أنتظر منك الجلسات الأخرى.

#8

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

الجلسة الثالثة:

توقفنا في الجلسة السابقة عند طريقة تعيين عنوان مؤشر الى مصفوفة لنسخ المصفوفة الى مصفوفة المؤشر ....

وذكرنا أن لها استخدامات في مناطق خاصة ...

ذكرنا أن الحسنة في ذلك هي السرعة المذهلة ...

ولكن العيب كما في المثال التالي ...

مثال 1:

[ int * q = new int [12

[ int w[12

w [0 ] = 1

w [1] = 1066

w [2] = 6

w [3] = 76

w [4] = 323

q = w

( void Set_Data ( int * z

}

z [ 0 ] = 10

z [ 1] = 10

z [ 2] = 10

}الدالة واضحة ...

الان سنستدعي الدالة أعلاه بعد عدة أسطر من الشيفرة ...

ونعلم أن قيم q أصبحت هي قيم w ....

وقررنا في هذه اللحظة أن نستدعي الدالة Set_Data لنعين قيم جديدة لأول 3 عناصر في q .....

( Set_Data ( q

المفاجيء هنا اننا غيرنا قيم المصفوفة المؤشرة q ... لكن الأمر السيء هو أن المصفوفة w ستتغير أيضا ... مع ان العلاقة بين q و w كانت في السابق ...

الى أن تعين قيمة عنوان لاخر يعني أن لهما نفس الخانة في الذاكر ة مما يعني أن أي تغيير في أي من المتغيرين سيسبب في تغيير الاخر ....

وليس ذلك هو المرغوب دائما .. لذا سنستعيض في بعض الأحيان بالنسخ العادي .. نسخ كل عنصر الى مقابله في المصفوفة الأخرى ...

" طبعا باستخدام حلقة For " لتسهل العملية ...

عموما تمرير المتغيرات بالعنوان له فوائد كبيرة عند تمرير الكائنات الكبيرة والضخمة الى احدى الدوال ... حيث ستستهلك عملية النسخ العادية الكثير من الوقت ....

لذا يلجأ المبرمجون عادة الى التمرير بالعنوان في نسخ الكائنات " Classes " ...

.........................

الفرق الثاني اننا قلنا :

حجم مصفوفة المؤشرات ديناميكي ولايشترط معرفته قبل وقت التشغيل ... أما حجم المصفوفات العادية فهو ثابت ويجب معرفته قبل وقت التشغيل ...

وهي المشكلة الأزلية التي لايمكن حلها مطلقا الا بالمؤشرات ...

وتواجه معظم مبرمجي السي ++ المبتدئين ....

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

" يوجد حل عملي أكثر بحيث لانطلب من المستخدم ان يدخل عدد الأرقام .. بل يبدأ مباشرة بادخال الأرقام سنتطرق له لاحقا ..." ,

في هذه اللحظة بعد ادخال عدد الأرقام سنحجز بها عدد من الخانات في الذاكرة كالتالي ..

نفرض أن المستخدم سيدخل عدد الأرقام في المتغير x ...

مثال 2 :

int x

cin >> x

[ int * q = new int [x

...

هكذا تم حل المشكلة فالمصفوفة المؤشرة q حجمها سيناسب عدد الأرقام التي ستدخل فيها ...

الان يأتي سؤال مهم ... لماذا لانستخدم المصفوفات العادية في ذلك ...

أي تكون ..

مثال 3:

int x

cin >> x

[ int q [x

...

المثال 3 لن يعمل ... وسيقوم المصرف باصدار خطأ بأن حجم المصفوفة q غير معروف ... لأن المصفوفات العادية يجب معرفة حجمها وقت تنفيذ البرنامج وليس وقت استخدام البرنامج ....

أم المؤشرات فلا مشكلة لديها في ذلك ...

ويعود السبب في ذلك الى ان المصفوفات العادية يتم تخزينها بالنسبة للبرنامج في ذاكرة المكدس الخاصة بالبرنامج ...

أما مصفوفات المؤشرات فانها تخزن في الذاكرة التكويمية .. ماعلينا ...

فقط عرفنا مايجب فعله ومالايجب ,,,,

............

الان الفرق الثالث :

يمكن الغاء الذاكرة الخاصة بمصفوفات المؤشرات في أي وقت من البرنامج ,, واذا نسيت الغاءها فلن يلغيها لك البرنامج الا بعد انتهاءه وتوقفه عن العمل ...أما الذاكرة الخاصة بالمصفوفات العادية فلا يمكنك الغاءها متى تشاء فالغائها يتم بشكل الي بعد الخروج من مدى الرؤية الخاص بها " SCOPE"....

..

بداية مالسبب الذي يدعوك لالغاء ذاكرة قمت بتخصيصها لبيانات معينة ؟؟

السبب دائما مايرجع الى استهلاك موارد الذاكرة مما لاطائل له .. يعني يمكن في لحظة معينة أن لانحتاج الى 1000000 متغير محجوز في مصفوفة من نوع int أي اننا سنحجز 4 ميجابايت من الذاكرة .... فبعد ان لانحتاج لهذه المتغيرات بتاتا يجب أن نحاول أن نقوم بالغاءها لنزيد من فعالية البرنامج ولافاح مجال الذاكرة للمتغيرات الجديدة ...

في هذا السيناريو لو كنا نستخدم المصفوفات العادية لأنتظرنا القدر ليأتي وينهي الدالة التي تم حجز المصفوفة داخلها ليقوم بتدمير المصفوفة .. "بعد الانتهاء من تنفيذ الدالة "

... قرر مصممو السي++ ايجاد حل لهذه الحماقة فقامو باختراع المؤشرا تلتعطي حرية وقت حذف النتغيرات ...

خذ المثال ..

مثال 4:

[ int * q = new int [1000000000

...

.

.

.

.الان بعد ان لانكون بحاجة الى المصفوفة المؤشره أعلاه سنقوم بحذفها كالتالي :

delete [] q

لاحظ الى الصيغة المستخدمة .. ولتعلم أن delete هو أمر اساسي جديد سيتغير لونه للأزرق وهو مضاد للأمر الاساسي new الذي نحجز به الذاكرة ....

بعد السطر أعلاه " سطر ال delete " سيتم تحرير الذاكرة فورا من الذاكرة ولن يعود هناك وجود في الذاكرة للمتغيرات التي كانت موجودة في المصفوفة المؤشرة بتاتا ...

لاحظ في صيغة ال delete أن المعقفات [] موجودة قبل المتغير q ... وهذا يعني أننا نريد حذف كل المصفوفة ...

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

q [ 2 ] =11سيحدث خطأ انتهاك للذاكرة .. لأننا أعدنا المصفوفة لفترة ماقبل التاريخ .. فيجب مرة أخرى قبل استخدام المصفوفة أن نقوم بحجز خانات لهذه المصفوفة...

[ q = new int [1233333333

..........

هذا شرح مختصر جدا للفرق بين المصفوفات والمؤشرات ...

سندخل في الأيام التالية لبعض المواضيع الأكثر حماسا ... وسيتعلق الأمر بالمتغير void والكائنات ... ومصفوفات التي تخزن قيما لمؤشرات ...ونأخذ نظرة سريعة على اللوائح المربطة " Link List " ثم سنتناول العلاقة بين الكائنات والوراثة وتعدد الأشكال والمؤشرات ... ونعرف كيف يمكننا أن ننشيء مؤشرا لمؤشر .. بحيث نقوم بتخزين عنوان العنوان ..... وكان الله في العون ...

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

تم تعديل هذه المشاركة بواسطة HGB في 18 نوفمبر 2003 في 02:14

banner_60_468.gif

NOTHING IS IMPOSSIBLE

#9

جدا جدا رهيب شرحك وياريت تكمل

وياريت بأمثلة وشوية دسمة يعني اسئلة امتحانات قوية :)

عشان نبغى نتعلم

بس بدون كلاس يعني انا ابغها بالسي شرحتا بالبلس بلس مش مشكلة \

لكن بصراحه مانبغى دوخة وكلاسات بعدين اهم شي الفهم:)

ألف شكر ومساعدين

#10

أستاذي HGB موضوع أكثر من رائع بارك الله فيك و زادك علما و معرفة

#11

مشكووووووور والله

اسلوب شرح جدا رائع

الله يوفقك وان شا الله تستمر معانا بها الطريقه

#12

بارك الله لك و عليك ...

و تقبل من عملك احسنه ... و زادك من عنده ..

انه على ذلك قدير ،،

لا اله الا الله .. محمد رسول الله

(* ربِ اجعلني مقيم الصلاة و من ذريتي ، ربنا و تقبل دعـاء *)

يا حيّ يا قيـوم ... برحمتك استغيث ... اصلح لي شأني كله و لا تكلني الى نفسي طرفة عين

#13

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

مرة أخرى أشكر الأخوة على التفاعل ,, ووفقكم الله وزادكم من علمه ,,,

أرجو أن لاتنسونا من صالح الدعاء هذه الايام عسى الله أن يرحمنا واياكم ,,,

..................................

كما أرى من بعض الطلبات ترجو الابتعاد عن الفئات والدوخة ,,,

أقول ان الفئات والمؤشرات تشكلان تحالفا قويا وتنتجان شيفرة ذات هندسه عالية المستوى ,,

والركيزة الثالثة في البرمجة كائنية المنحى وهي تعدد الاشكال والدوال الوهمية تعتمد أساسا على المؤشرات ... ولاتعمل مطلقا دون المؤشرات ,,,

حسنا , لابأس سنبتعد عن " وجع الرأس " قليلا جلستين أو ثلاث حتى نغطي مواضيع أخرى متعلقة عن المؤشرات ,,,

ولكن لنا عودة ....

..................................

الجلسة الرابعة :

سنتحدث في هذه الجلسة عن أمرين مهمين في المؤشرات ...

الأول : اذا أردنا من المستخدم أن يدخل اسما في متغير طبعا من النوع char .. لكننا لانعرف طول الاسم الذي سيقوم المستخدم بادخاله .. في هذه الحال يلجأ المستخدم المبتديء لاستعمال المصفوفات وجعل حجم المصفوفة كبير لدرجة حتى تسع لأي اسم مهما كان ...

طبعا لهذه الطريقة سيئة أنها تبذر الذاكرة بشكل فعال عند استخدام عدة أسماء ...

الحل : الحل في أن نجعل المصفوفة عبارة عن مؤشر ونحجز للمؤشر الحجم المطلوب في وقت التشغيل " وهي ميزة للمؤشرات على المصفوفات "...

يمكن بأن ننشيء مؤشرين ونحجز للأول حجما كبيرا حيث يتسع لأي اسم مهم كان كبيرا ..

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

الان لنفرض اني أريد من المستخدم أن يدخل عدة أسماء وليس واحدا فقط ... وطبعا كل اسم لانعرف حجمه وطول كل اسم لانعرف حجمه ... طبعا لو استخدمنا المصفوفات العادية فقط لكونا مصفوفة ثنائية البعد .. البعد الأول لعدد الاسماء والثاني لعدد الاحرف في كل اسم ,,,

مثال 1:

[ char X [200][3

هنا حجزنا 200 خانات في الذاكرة ل200 اسم .. وكل اسم طوله 30 خانة...

هذه الطريقة أيضا ليست عملية ... عدد الاسماء كبير لدرجة قد تبذر الذاكرة بشكل كبير ,,

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

كالتالي ..

مثال 2:

[ char *X[200

لاحظ * قبل اسم المتغير X هذا يعني أننا سنكون مصفوفة حجمها 200 عنصر هذه المصفوفة ستخزن عناوين وليس قيما كما في المعتاد !! هذه هي مصفوفة المؤشرات ..

بالمقارنة مع مثال المصفوفة ثنائية البعد يمكننا تشبيهها [ان البعد الاول هو 200 والثاني ديناميكي في حالتنا .. أي سنقوم بادخاله لاحقا " وهي ميوة للمؤشرات ",,

سنقوم بادخال طول الاسم كل مرة قبل أن نخزن الاسم في المؤشر بهذه الطريقة سنكون قد وفرنا الكثير من الذاكرة بدل أن نفترض ان طول كل اسم 30 خانة كما في مثال المصفوفة ...

بعد التعريف أعلاه يكون الاستعمال كالتالي ,,

[X[0] = new char[18

[cin >> x[0

[ X[1] = new char[22

[ cin >> x [ 1

[/color[

وكذا حتى ندخل جميع ااسماء .. طبعا سنستخدم حلقة لفعل ذلك ,,,

الان لنفرض أنني أريد أن لأعرف أيضا عدد الاسماء حتى أخصص الذاكرة بأفضل صورة ...

في هذه الحال يجب علي أن أحل المشكلة الاولى وهذه المشكلة سويا ... طبعا عدد الاسماء ديناميكي اذن سأستخدم مؤشرا لأحجز العدد المناسب لعدد الاسماء في وقت التشغيل ... لكن كيف أعل ذلك ؟؟ اليك هذا التدوين الذي قد يبدو غريبا للوهلة الاولى ..

مثال 3:

char ** X

وهذا مايدعى بمؤشر المؤشر أو عنوان العنوان ...في هذه الحال سننشيء عدد خانات ديناميكي لنخزن فيه عناوين فقط ...

الان كيف يمكننا أن نحجز لهذا الشيء عدد الخانات ؟؟

مثال 4:

char ** X

[ X = new char* [200

( ++ for ( int q=0 ; q < 200 ; q

[ X[q] = new char [30

الان المثال أعلاه أصبح جاهزل ليعامل كما يعامل المثال 1 ... لكن بالطبع يمكن أن أغير في الحجم كما أشاء بحيث يكون متغير وليس ثابتا ,,,

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

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

قد تتحير فعلا في بعض الحالات النادرة عن كيفية انجاز مهمة بدون مؤشر المؤشر ...

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

بالنسبة للعبة وصلت الى مستوى شبه ناضج وبدأ الكمبيوتر " بقدرة قادر " بالتفكير وتوقع النتائج ل 4 أدوار ... وهي لابأس بها .. حيث يكون عدد الاحتمالات الكلي كبير جدا قد يصل 10 مليون احتمال "لخمسة أدوار" ,, وكان الجهاز وقتها يحسب 180 ألف احتمال في الثانية " وهو رقم سيء بالنسبة لألعاب الشطرنج الاخرى " واكتشفت السبب بعد فترة لكن بعد فوات الاوان وسأذكر السبب لاحقا وهو يتعلق بالمؤشرااات ,,, كما قلت سابقا من لايجيد اللعب بالنار فلا يلعب بالمؤشرات ,,, تحتاج الى كثير من التجارب قبل الشروع في التنظير في المؤشرات ,,,

عموما لانخرج من الموضع فقط حببت أن أوضح مثال قد يكون واقعي لاستخدام المؤشرات المتداخلة ....

الان قبل ان نتحرك أقول أنه يمكنك أن تنشيء مؤشر لمؤشر المؤشر ... وزد على ذلك كما تشاء ...

مثال 4:

int ****W

لكن لن تحتاج لذلك الى اذا أحببت أن تقوم ببعض أعمال فرانكنشتاين !!!

.......................................

VOID وماأدراك ما VOID !! وهو نوع المتغيرات العقيم الذي لايصلح لشيء في عالم النتغيرات .... لكنه كبير هنا في عالم المؤشرات ...

اذا أردن أن نعين عنوان لعنوان أي متغير مؤشر الى متغير مؤشر اخر .. يجب أن يكون كلا المتغيرين من نفس النوع ,,, خذ المثال ...

مثال 5:

int * x

int * e

float *w

float * y

char *t

char * u

e = 10 *

x = e // OK

x = w // ERROR Cant covert From float to int

w = y // OK

t = u // OK

w = t // ERROR cannt convert fromchar to float

وهكذا كما في المثال أعلاه يجب ان يكون تعيين المؤشر الى نفس نوعه ...

الا void ,,,, يمكن لل لمؤشر ال void أن يقبل أي نوع من الانواع الاخرى ,,,

مثال 6:

void * R

int * q

double * u

char * c

R = q // OK

R = u // OK

R = c // OK

لاحظ أني قلت مؤشر ال void وليس متغير ال void لأنه لايمكنك انشاء متغير من النوع void أقصد متغير عادي ليس مؤشرا كما يلي

void F

المثال أعلاه سيعطي خطأ من المصرف بأه لايمكن انشاء متغير من النوع void ... يجب أن يكون فقط مؤشر أأو مؤشر لمؤشر أو أكثر من ذلك كما تشاء ....

العظمة في استخدام مؤشرات void هي في أنك يمكن أن تنشيء مؤشر وتحجز له مثلا 100 خانة ولاتقتصر بوضع نوع واحد فقط من المتغيرات في المصفوفة !! يمكنك انشاء مصفوفة تضع في متغيرات مخلوطة من جميع الانواع .. يعني العنصر الاول يكون فيه قيمة int والثاني float والثالث int والرابع class قمت انت باشاءه .. وهكذا .. لكن لايمكنك أبدا فعل ذلك بالمتغيرات العادية .. كل نوع يخزن نوعه فقط ... ولامجال للتلاعب ...

ماعدا الفئات فلها حالة خاصة وهي تعدد الاشكال يمكن أن تقدم هندسة كهذه التي لدينا هنا في void سنتناولها لاحقا ,,,

كما ذكرت تستخدم مؤشرات void في تكوين مصفوفة لأنواع مخلوطة ... أو تمرير متغيرات لدوال تتوقع عدة أنواع من البيانات ,,,,

وأتابع وأقول بالنسبة لمصفوفة المؤشرات تكمن المشكلة عند محاولة استرجاع البيانات التي في المصفوفة ... فلا نعلم نوع البيانات الموجود في كل خانة ... يمكنك لحل هذه المشكلة أن تضع فهرسا لنوع المتغيرات في كل خانة بحيث تسترجع النوع فورا كما تريد ,,, مثلا لو علمنا أن المتغير الاول في المصفوفة void التالية int والثاني char والثالث double لاسترجاعها سنقوم بالتالي ...

مثال 7:

void * V

// Putting Data Here

.

.

.

[ cout << ( int ) v[0

[ cout << ( char) v[1

[ cout << ( double) v[2

طبعا باستخدام الامر () typeid(TYPE). name ستحصل على النوع ...

سأوضح ذلك في الجلسة المقبلة وسأضع بنية للتعامل مع مصفوفات المتغيرات المختلفة .. لكن بشرط كونها من الانواع الاساسية فقط ..

قد توجد طريقة أخرى لعملها مع الأنواع الجديدة الفئات والبنى .. لكن ليست في حدود علمي حتى الان,,,

.....................................

في الجلسة التالية سنتناول مثلا لمصفوفة void وبنية للتعامل بشكل عملي مع مصفوفات النتغيرات المختلفة ,,,

ثم سنتناول موضوع ال مؤشرات الثابتة const ....

ونحاول أن لقي الضوء على مشكلة واجهتني تتعلق بالاداء عند استعمال المؤشرات ,,,

...................................

الى ذلك اللقاء أترككم في رعاية الله ...

تم تعديل هذه المشاركة بواسطة HGB في 22 نوفمبر 2003 في 03:30

banner_60_468.gif

NOTHING IS IMPOSSIBLE

#14

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

ياشباب في خطا فقط أريد تصحيحة في الجلسة الاخيرة ...

مثال 7:

void ** V

[ V = new void* [10

// Putting Data Here

.

.

.

[ cout << *( int* ) v[0

[ cout << *( char*) v[1

[ cout << *( double*) v[2تصحيح المثال السابع ,,,,

تم تعديل هذه المشاركة بواسطة HGB في 22 نوفمبر 2003 في 06:32

banner_60_468.gif

NOTHING IS IMPOSSIBLE

#15

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

في الجلسة الاخيرة ذكرت أننا سنتناول موضوع مصفوفة VOID ذات الانواع المختلفة في هذه الجلسة ,, لكن كتابة مصفوفة كتلك ليس فعالا دون استخدام الفئات Class لتغليف بعض المهام الجانبيه الخاصة بالمصفوفة ... لذا سنأجل موضوعها قليلا حتى نتناول في جلسات أخرى في موضوع اخر سأطرحه في هذا المنتدى موضوع الفئات Classes .. ثم نعود لنتابع هنا مرة أخرى ....

الجلسة الخامسة :

تدرجنا حتى الان في مواضيع المؤشرات من الابتدائية وبعض الامور التي تستخدم كثيرا .. وحتى الامور المتوسطة التي لاتكاد تذكر في الكتب كموضوع *Void و موضوع مؤشر المؤشر **int ... ولأن هذه المواضيع شحيحة جدا في الكتب قررت أن أستعرضها هنا عسى أن تنفع أحد السائلين ...

........................................

سنتناول الان عدة أمور ,,,

أولا : سنكمل موضوع VOID ولكن ليس مصفوفات VOID وانما الدوال التي تستلم بارمترات من النوع VOID ..... لماذا توجد دوال هكذا ؟؟ مالداعي لتمرير بارمتر من النوع VOID الى الدالة ؟؟ وكيف يمكن للدالة التعامل معه ؟؟؟

الان لنفرض أننا نريد أن نكتب دالة فلنقل تقوم بضرب رقمين فقط ليس الا بحيث نمرر لها بارمترين وهما الرقمين المراد ضربهما ولكن يجب أن تعيد الدالة متغيرا من نفس نوعي البارمترين .... وفي نفس الوقت أريد ان أجعل هذه الدالة تتعامل مع كل الانواع int , float , double .. وغيرها ... ولا أريد أن أكتب سوى دالة واحدة وتقوم في نفس الوقت بالعمل الصحيح تماما ... ماذا علي أن أفعل ؟؟؟ طبعا من الواضح أن من أراد كتابة كهذه دالة يجب أن يكتب 6 دوال مختلفة بنفس الاسم لكن بأنواع بارمترات مختلف لتغطية كل الانواع الاساسية ... بحيث يتم استدعاء الدالة المناسبة كل مرة .. لكن أنا لا أريد ان أتعب نفسي وأكتب 6 دوال ولاأريد تعقيد الشيفرة وزيادة عدد الاسطر مما لاطائل منه ,,

وأريد أن أكتب فقط دالة واحده ...

فكل ماسأفعله أن الدالة ستكون كالتالي ,,,

مثال 1:

( void* MULT (void* x, void* y, char* type

{

( switch( type

}

: ' case ' int

( return & ( *(int*)x) * ( *(int*)y

break

: ' case ' float

( return & ( *(float*)x) * ( *(float*)y

break

: ' case ' double

( return & ( *(double*)x) * ( *(double*)y

break

}

{

وهكذا نكون قد دعمنا 3 أنواع أساسية وتكفي .. يمكن أضافة الباقي بوضع case جديدة ..

المهم لاحظ هنا أن الدالة ستعيد القيمة المناسبة دائما حسب النوع الممرر لها ..

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

فعلا هذا أسهل ... لكن اذا مررنا الى الدالة مصفوفة من النوع ** VOID تحمل عدة أنواع مختلفة int , float , long ونريد أن نوجد مثلا أكبر رقم من الارقام الممررة بحيث نعيده بنوعه .." يعني أكبر رقم int نعيده int واذا كان float كذلك وغيره .." ..

وفي نفس الوقت لانعلم عدد الارقام المدخلة !!! في هذه الحالة يستحيل حل هذه المشكلة الا بمصفوفات ** VOID ....

مثال 2:

( void* MULT (void** x,char** type ,int size

ستكون الدالة كما في السابقة .. لن أدخل في تفصيلها ... لكن ان رغبتم في ذلك كتبتها لكم ...

عموما لاداعي بأن تقلق مطلقا بشأن هذه النجوم الكثيرة 99 % من استعمالات السي++ وان لم تكن كلها يمكن أن تكتب بستخدام مؤشرات فقط " نجمة واحدة" ... وليس عدة نجوم ... فقط رغبت في أن أوضح كل شيء هنا ولم أقصد فقط تبسيط المنهج .. وهو الواضح من عنوان الدروس " نظرة تحليلية مفصلة ",,,, لكن سنبعد عن التعقيد في الجلسات القادمة حتى نرجع لاحقا الى الفئات ...

.............................................

أمر اخر يتعلق ب const وارتباطه بالمؤشر . ...

بداية ماذا يعني const ... لمن لايعرف فان const هو أمر أساسي في السي ومعناه ثابت .. ومن اسمه فهو يجعل المتغير الذي بعده ثابت طوال فترة البرنامج ولا يمكن أبدا تغيير قيمته .. بل يجب تعيين قيمة له وقت التمهيد فقط ..

مثال 3:

const int y = 0

y = 7 // ERORR y Is Constant Var

الان y لايمكن أن تتغير قيمتها من الصفر أبدا طوال وقت تنفيذ البرنامج ....

الان مادخل const بالمؤشرات .. كما خمنت يمكن أن نجعل المؤشر ثابت ....

وموضوع const مع المؤشرات متعب قليلا فيمكن جعل المؤشر المطلوب ثابت أو جعل القيمة التي يؤشر اليها المؤشر ثابته ...

مثال 4:

int w = 33

int q = 10

const int * y = & q

هنا القيمة التي يؤشر اليها المؤشر y ثابته يعني y* ثابته ... وليس y هي الثابتة ...

مثال 5:

y = 55 // ERROR *y is const *

y = & w // OK You Can change Th Value

y*

لايمكن تغيررها بعد تمهيدها كما في المثال 3 .. أما y نفسها فيمكن تغييرها ...

أما المثال التالي ,,,,

مثال 6:

int w = 33

int q = 10

int * const y = & q

هنا العكس يمكن تغيير القيمة y* ولايمكن تغيير العنوان y ...لاحظ لمكان توضع const في المثالين 6 و 4 ,,,,

مثال 7:

y = 55 // OK You Can change Th Value*

y = & w //ERROR y is Const

الان الامر واضح اذا كانت const في البداية كما في المثال 4 فان القيمة التي يحملها المتغير ثابته ... أما اذا كانت بعد نوع المتغير كما في المثال 6 فهذا يعني أن عنوان المتغير هو الثابت ...

أخيرا يمكن وضع const في كلا الموضعين لكي يصبح كل من المؤشر وقيمة المؤشر ثابته ...

مثال 8:

int w = 33

int q = 10

const int * const y = & q

y = 55 //ERROR *y is Const*

y = & w //ERROR y is Const

هذا الموضوع باختصار شديد .. ...........

........................................

أخيرا بالنسبة لما يتعلق بالاداء فان سرعة عمل البرنامج وهو يعمل بالمؤشرات أقل من سرعته وهو يعمل بالمصفوفات العادية بمدة زمنية لاتتجاوز 1 من كل 10 مليار ....

بالعربي لافرق بينهما مطلقا ... وهذا ماتأكدت منه وقد كنت أشكك في ذلك حتى جربت بنفسي ,,,

عموما المشكلة الوحيدة في الاداء يمكن أن تحدث بسبب المبرمج ... حيث لو كتبت كود برنامجك بحيث وضعت الكثير من عمليات حجز للمتغيرات ..

[ w = new int[100

الامر السابق يأخذ وقتا طويلا .. فيجب محاولة الابتعاد عن تخصيص الكثير من الذاكرة كلما أمكن ذلك ...

فلو كان الامر السابق داخل حلقة تعمل 40000000 مرة سيأخذ البرنامج وقتا طويلا جدا مما يسبب في فشل أداء البرنامج بشكل كبير ...

وكل ماحاولت الابتعاد عن حجز كميات الذاكرة كان الاداء أفضل ...

عموما حاول بقدر الامكان أن تحجز لمتغيراتك كلها مرة واحدة ... أو عدة مرات قليلة اذا استلزم الامر ذلك .. لكن اياك أن تضع عمليات حجز داخل حلقات Loop كبيرة لكميات كبيرة ...

واذا اضطررت لذلك فيجب عليك النظر في هندسة البرنامج ككل من البداية ...

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

وقد عرفت سبب المشكلة لكن بعد فوات الاوان ...

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

ولكن اذا حجزت مرات قليلة داخل حلقة و لأحجام ضخمة سيكون الاداء هو الاسوأ على الاطلاق,,,

الخلاصة : احجز لعدد مرات متوسط لكميات متوسطة ,,,,

حيث المتوسط = 10000

.........

وبالمناسبة هذه المعلومات قيمة جدا ولم أجد لها مثيلا في الكثير من الكتب ,,, فقط يجب عليك القيام ببعض التجارب لتعرف ماهو أفضل ...

أو محاولة قراءة تعليمات الاسمبلي الخاصة بعمليات الحجز الديناميكي ... للخروج بأفضل النتائج في سرعة تنفيذ البرنامج ...

.................................................

يوجد أمر واحد لم أذكره حتى الان وهو أحد أوجه الخطورة أيضا ومن الاسباب التي دعت Microsoft لتبني ملمم النفايات ليدير عمليات تنظيف المؤشرات المحجوزة بشكل الي ,,, حيث يجب دائما في المؤشرات عندما نحجز بالامر new أن نضيف الامر delete لحذف البيانات التي قمنا بحجزها .. والا سيتعطل البرنامج كله اذا كانت كمية البيانات المحجوزة كبير ...

وهذا هو عمل ال GC " ملمم النفايات " الذي يهتم باستدعاء الامر delete بشكل الي عند الحاجة ... ولكن هذ ال GC غير متاح في لغة السي /سي++ وانما يوجد في ال c# , JAVA , C++.net ...

وسأتناول هذا الموضوع ببعض التفصيل الجلسة المقبلة ....

والى اللقاء ...

Test_.cpp

تم تعديل هذه المشاركة بواسطة HGB في 26 نوفمبر 2003 في 05:28

banner_60_468.gif

NOTHING IS IMPOSSIBLE

#16

هلا أخوي ،، :)

الله يسعدك ،، لا تتأخر علينا ،،

ترانا متشوقين نعرف أكثر ،،

أختك ،، العصامية ،، :)

#17

ألف مليون شكر

#18

السلام عليكم

طبعا كلمات الشكر قليلة في حقك على هذا الشرح الوافي ولكن لا املك إلا دعوات صادقة في ظهر الغيب.

ولكن لي طلب بسيط إن أمكن تزويدنا بــ أمثلة يفضل فيها استخدام المؤشرات عن غيرها

دون حلها حتى لانأخذ من وقتك فقط أريد مثالا .

ولك خالص شكري وننتظر المزيد.

iwo3e5b2.gif

اللهم اغفر لجميع المسلمين والمسلمات الاحياء منهم والأموات.

#19

السلام عليكم

أخي الكريم شكرا لك على الدروس الرائعة و لكن عندي بعض الأسئلة: :lol:

1-بالنسبة لحجز مكان المؤشر في الذاكرة كنا قد استخدمنا :

int *x;

x=new int;

*x=50;

و لكن نحن نعلم أن x* في هذه الحالة هي القيمة و x هو المكان في الذاركة طيب نحن لو كتبنا هكذا :

int *x;

x=new int;

*x=50;

cout<<*x<<"\n";

cout<<x<<"\n";

cout<<&x<<"\n";

سيظهر موقعين مختلفين الأول هو خاص ب xو الثاني ب x& فإذا كان موقع المؤشر هوx ما هو الموقع x&إذا ..

2-نحن عندما حجزنا مصفوفة أماكن في الذاكرة للمؤشر عن طريق الكود :

int *x;

x=new int[10];

طيب نحن لو طبعنا مكانx في الذاكرة كالتالي:

int *x;

x=new int[10];

cout<<x<<"\n";

رح يظهر موقع واحد و ليس 10 مواقع ..هذا الموقع الي ظهر هو موقع أي خانة من الخانات ال10.

#20

1)

cout <<*x;
سيطبع القيمة نفسها 
cout<<x;
سيطبع عنوان القيمة
cout<<&x;
سيطبع عنوان عنوان القيمة

2)بالنسبة للسؤال الثاني عند طباعة :

cout <<x;
سيطبع عنوان الخانة الأولى والتي هي تساوي :
cout <<&x[0];

banner_60_468.gif

NOTHING IS IMPOSSIBLE

#22

شكرا اخي جدا جدا على هذا المجهود الرايع

وبارك الله فيك ووفقك

#23

موضوع ممتاز...

لماذا لم يتم تثبيته؟

#25

[.ddnnجزاكم الله خير الجزاء والف شكر على المجهود الرائع

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