430 likes | 1.3k Views
التعامل مع المؤشرات Pointers في لغة C++. مفهوم مواقع الذاكرة عنونة المتغيرات في الذاكرة و الحصول على عناوين المتغيرات في الذاكرة ما هو المؤشر ؟ الإعلان عن المؤشر في البرنامج و استخدامه و لكن ما هي فائدة المؤشرات ؟؟؟ الوصول إلى محتوى القيمة التي يشر اليها المؤشر المؤشرات و المصفوفات
E N D
التعامل مع المؤشرات Pointers في لغة C++ مفهوم مواقع الذاكرة عنونة المتغيرات في الذاكرة و الحصول على عناوين المتغيرات في الذاكرة ما هو المؤشر ؟ الإعلان عن المؤشر في البرنامج و استخدامه و لكن ما هي فائدة المؤشرات ؟؟؟ الوصول إلى محتوى القيمة التي يشر اليها المؤشر المؤشرات و المصفوفات المصفوفة مؤشر ثابت ، لكن المؤشرات متحركة ! المؤشرات والدوال (ارسال القيمة بالعنوان الى الدالة) المؤشرات و سلاسل الحروف مصفوفة المؤشرات
مواقع الذاكرة • لنعتبر ان شكل الذاكرة مثل صناديق البريد ، حيث تمثل كل خانة موقع في الذاكرة و تقوم بتخزين قيمة وحيدة (قد تكون صحيحة int او حقيقة float او char) ، كل موقع في الذاكرة له عنوان ، و يتم تمثيل عناوين الذاكرة باستخدام النظام الست عشري من باب تسهيل كون انه في الاصل يمثل باستخدام النظام الثنائي ، فعلى سبيل المثال لتمثيل الخانة رقم 15 باستخدام النظام الثنائي فإننا سوف نحتاج إلى اربع خانات هي 1111 على عكس النظام الست عشري الذي يمثلها بخانة واحدة هي F .
عنونة المتغيرات في الذاكرة • عند تعريف المتغيرات في البرنامج ، فإنها تأخذ حيزاً في الذاكرة بما يتناسب مع حجمها فعلى سبيل المثال المتغيرات من نوع int تشغل 2 bytes على عكس المتغيرات من char و التي تشغل 1 byte. • لاحظ هذا المثال : main() { int i = 19; char c = 'X'; float f = 3.453; } • السؤال الآن .. هل يمكن ان نتعامل مع عنوان المتغير في الذاكرة بدل ان نتعامل معه بشكل مباشر ؟؟ ثم كيف يمكن ان نحصل على عنوان متغير ما في الذاكرة ؟
عناوين المتغيرات في الذاكرة • كي نستطيع ان نعرف عنوان متغير ما في الذاكرة نقوم بإضافة الإشارة & قبل المتغير و هي تعني عنوان المتغير (Address Of ) ،لاحظ : #include "iostream.h" #include "conio.h" void main() { int x; cout << "Enter Number: "; cin >> x; cout << "\nThe Number is:" << x << endl; cout << "\nand The address in memory is: "<< &x << endl; getch(); } • عند تجربة البرنامج تظهري لي النتيجة التالية : The Number is: 5and Thr address in memory is: 0x0012FF7C لاحظ ان الناتج من الممكن أنا يختلف من كمبيوتر إلي كمبيوتر آخر, ومن تنفيذ إلي أخر (لماذا) يطبع محتوى المتغير يطبع عنوان المتغير
ما هو المؤشر ؟ • تمثل المؤشرات احد أهم الإمكانيات التي توفرها لغة C++ ... اول سؤال سيبدر إلى ذهنك ما هو المؤشر و إلى ماذا يشير ... • و كما هو الحال مع أي متغير ، فإننا قبل ان نستخدم المؤشر في برنامجنا لابد لنا من ان نعلن عنه .. • كل المتغيرات من نوع مؤشر لها نفس الحجم في الذاكرة و هو حجم العنوان الذي تحتويه (فهي مجهزة لتخزين العناوين) .. المؤشر عبارة عن متغير يحتوي على عنوان في الذاكرة (و ليس قيمة عادية) ، وهو يشير الى العنوان الذي يحتويه و بالتالي فهو يشير إلى متغير اخر ...
نوع القيمة التي يشير اليها المؤشر الإعلان عن المؤشر في البرنامج و استخدامه • يتم الاعلان عن المؤشر في البرنامج بتحديد نوع القيمة التي يشير اليها (أي هل يشير إلى قيمة من نوع int-char-float ..) ثم اضافة العلامة (الرمز) نجمة * ثم اسم المؤشر ، لاحظ هذا المثال : • لا يمكن التعامل مباشرة مع المؤشر في البرنامج قبل ان نسند اليه عنوان متغير ما كما يلي : int i; iPtr = & i • لاحظ انه من المتعارف عليه(و ليس شرط) ان تبدأ اسم اسماء المؤشرات بالمتغير الذي تشير اليه و تنتهي بالكلمة Ptr ، و لابد ان يشر المؤشر إلى قيمة لها نفس نوعه . int *iPtr; // int* iPtr; نجمة يليها اسم المؤشر
1020 1022 العناوين في الذاكرة 10032 … 88 5 … 1024 … y yPtr الإعلان عن المؤشر في البرنامج و استخدامه • اتفقنا ان الرمز & يعيد عنوان المتغير في الذاكرة ، لاحظ هذا المثال : int y = 5; int *yPtr; yPtr = &y; // y تأخذ عنوان yPtr • لاحظ كيف سيكون شكل المتغيرات في الذاكرة : • لاحظ من المثال ان انشاء المؤشر يتم على مرحلتين الاولى نعلن فيها عن المؤشر yPtr و الثانية نستد اليه عنوان متغير في الذاكرة و ذلك يعني ان yPtr سوف تشير إلى المتغير y في الذاكرة و بالتالي من الممكن التعامل مع y بشكل غير مباشر عن طريق yPtr . y yPtr 5
و لكن ما هي فائدة المؤشرات ؟؟؟ • لنتخيل اننا نريد ان نخزن رقم هاتف شخص ما في ذاكرة هاتف النقال و ليكن هذا الشخص "محمد" ، فإننا سوف نقوم اولاً بإدخال رقم الهاتف ثم سندخل بعدها اسم "محمد" .. و الآن اذا اردنا نتصل بـ "محمد" فإننا سوف نبحث عن اسم "محمد" و نقوم بالاتصال به .. و لكن في الحقيقة لا يوجد رقم تلفون رقمه "محمد" و لكن اسم "محمد" هذا يشير على مكان في ذاكرة الهاتف يحتوي على الرقم المراد الاتصال به .... • في المثال السابق ، يمثل لنا الاسم ”محمد ” المؤشر الذي يحتوي على رقم الهاتف و بدلاً من ان نتصل بإدخال الرقم فإننا نستخدم ”محمد“ كونها تسهل لنا الامور و تسرعها ...
و لكن ما هي فائدة المؤشرات ؟؟؟ • هناك حالات لابد معها من استخدام المؤشرات ، و هناك حالات لا تشترط ذلك ، و لكن الامور تكون اسهل و اسرع في حال ان استخدمنا المؤشرات.. من فوائد المؤشرات : • الوصول إلى عناصر المصفوفة . • تمرير المعاملات إلى دالة باستخدام العنوان (Passing By Reference) . • ارسال المصفوفة او سلاسل الحروف إلى الدوال . • التحكم بمساحة اكبر من الذاكرة. • في التعامل مع هياكل البيانات (Linked List , ……) .
الوصول إلى محتوى القيمة التي يشر اليها المؤشر • عندما نعلن عن مؤشر فإننا نستخدم الصيغة (int *varPtr) حيث ان اسم المؤشر هو varPtr ، و لكننا نستخدم الصيغة *varPtrداخل البرنامج (كتعبير) من اجل الوصول إلى محتويات المتغير الذي يشير اليه المؤشر varPtr ، لاحظ هذا المثال : void main () { int *varPtr; Int var = 20 ; varPtr = &var; cout << *varPtr; // 20 cout << var ; // 20 } • و هكذا عليك بالانتباه إلى : • استخدام *varPtr في جملة الاعلان int *varPtr; تعني اننا نعلن عن مؤشر اسمه varPtr و يشير إلى قيمة من نوع عدد صحيح . • استخدام *varPtr في البرنامج كتعبير cout << *varPtr; تعني اننا نتعامل مع محتوى القيمة التي يشير اليها المؤشر varPtr . يستخدم اسم المؤشر مسبوق بالنجمة في البرنامج ، من اجل الوصل إلى محتويات المتغير الذي يشير اليه.
الوصول إلى محتوى القيمة التي يشر اليها المؤشر • تتبع البرنامج التالي ، و حاول ان تستنتج شكل المخرجات : int main (){ int value1 = 5, value2 = 15; // الاعلان عن متغيرين صحيحين int* p1; // الاعلان عن مؤشر يشير إلى قيمة من نوع عدد صحيح int* p2; // الاعلان عن مؤشر ثاني يشير إلى قيمة من نوع عدد صحيح p1 = &value1; // جعل المؤشر الاول يشير إلى المتغير الاول p2 = &value2; // جعل المؤشر الثاني يشير إلى المتغير الثاني *p1 = 10; // تخزين القيمة 10 في المتغير الذي يشير اليه المؤشر *p2 = *p1; // مساواة قيم المتغيرات التي تشير لها المؤشرات p1 = p2; // جعل المؤشر الثاني يشير إلى المتغير الذي يشير اليه المؤشر الاول *p1 = 20; // تخزين القيمة 20 في المتغير الذي يشير اليه المؤشر الاول cout << "value1==" <<value1<<"/ value2=="<< value2; return 0; } • النتيجة هي : • Value1 is 10 • Value2 is 20
المؤشرات و المصفوفات • هناك علاقة خاصة بين المصفوفات و المؤشرات ، لاحظ كيف يمكن الوصول الى عناصر المصفوفة A في المثال التالي : int A[5] = {7 ,2 ,6 ,4 ,3} ; for(int i=0;i<5;i++) cout<<A[i]; من خلال المثال السابق نلاحظ انه تم الوصول الى عناصر المصفوفة عن طريق ذكر اسم المصفوفة (ال1ي يمثل عنوانها في الذاكرة) و رقم العنصر المراد الوصول اليه (تذكر ذلك لاحقاً) . • انتبه معي الى التالي : • اسم المصفوفة يدل على موقعها في الذاكرة ، اذن فأسم المصفوفة عبارة عن مؤشر ، و هذا المؤشر يشير الى اول موقع في المصفوفة ، أي العنصر A[0] ، و بالتالي فمن الممكن القول ان (ليس بشكل برمجي و لكن كمفهوم): A[0] = A
المؤشرات و المصفوفات • لاحظ كيف يمكن الوصول الى عناصر المصفوفة A باستخدام المؤشرات في المثال التالي : int A[5] = {7 ,2 ,6 ,4 ,3} ; Int* aPtr; aPtr = A; // تم اسناد عنوان المصفوفة عن طريق اسمها الى المؤشر for(int i=0;i<10;i++) cout<<*( aPtr + i ); • في المثال السابق لاحظ : • تم الوصول الى محتويات المصفوفة عن طريق *( aPtr + i ) حيث ان المؤشر aPtr يشير الى المصفوفة A (الى اول موقع فيها و الذي رقمه 0) ، و النجمة قبل اسم المؤشر تعني محتوى القيمة التي يشير اليها المؤشر، و من اجل الانتقال الى العنصر التالي في المصفوفة نقوم بإضافة قيمة i (و التي تكون متسلسلة 1 2 ....) ، لان اضافة قيمة i يعمل على تحريك المؤشر بمقدار 2 bytes (لأن المؤشر من نوع int) ، و بالتالي الانتقال الى الخانة التالية .
المصفوفة مؤشر ثابت ، لكن المؤشرات متحركة ! • لاحظ التغير الذي طرأ على المثال السابق int A[5] = {7 ,2 ,6 ,4 ,3} ; Int* aPtr; aPtr = A; // تم اسناد عنوان المصفوفة عن طريق اسمها الى المؤشر for(int i=0;i<10;i++) cout<<*( aPtr ++); • في المثال السابق لاحظ : • تم اسناد عنوان المصفوفة A الى المؤشر aPtr لذا اصبح من الممكن الوصول الى عناصر المصفوفة عن طريق هذا المؤشر ، في المثال السابق (الصفحة السابقة) استخدمنا الصيغة *( aPtr + i ) لتحريك المؤشر بين عناصر المصفوفة ، و في هذا المثال استخدمنا صيغة اخرى لتحريك المؤشر بين عناصر المصفوفة و التي هي *( aPtr ++) ، و كلا الصغتين صحيح لانه يضيف مقدار 2 bytes الى المؤشر aPtr مما يحركه الى العنصر التالي . • لكن هل من الممكن عمل *(A ++) ، أي التحريك و الانتقال باستخدام اسم المصفوفة (الذي هو مؤشر الى المصفوفة) ؟؟ الاجابة لا ، لأن التعبير A و الذي يدل على عنوان المصفوفة في الذاكرة عبارة عن موقع ثابت لا ينبغي تغيره او العبث به .