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

الدرس السابع المؤشرات Pointers

اذهب الى الأسفل

الدرس السابع المؤشرات  Pointers  Empty الدرس السابع المؤشرات Pointers

مُساهمة من طرف Admin الثلاثاء أغسطس 06, 2019 12:09 pm

[SIZE="4"]المؤشرات Pointers
المؤشرات هي غول البرمجة بلغة C++ (و C)، سنحاول في هذا الدرس إزالة الغموض عن المؤشرات وإظهار الاستخدامات العملية لها في برمجة C++ .

ما هي استخدامات المؤشرات ؟
فيما يلي بعض الاستخدامات الشائعة:
• الوصول Accessing إلى عناصر مصفوفة .
• تمرير Passing الوسائط إلى دالة عندما تحتاج الدالة إلى تعديل الوسيط الأصلى .
• تمرير Passing المصفوفات والسلاسل إلى الدوال .
• الحصول على Obtaining الذاكرة من النظام .
• إنشاء Creating هياكل البيانات مثل القوائم المرتبطة linked lists .

العناوين والمؤشرات Addresses and Pointers
الأفكار وراء المؤشرات ليست معقدة. إليك المفهوم الرئيسي الأول : لكل بايت byte في ذاكرة الكمبيوتر عنوان address . العناوين هي أعداد numbers ، تمامًا كما هي الحال بالنسبة للمنازل في الشارع . تبدأ الأرقام من 0 وتصعد ، 1 ، 2 ، 3 ، وهكذا. إذا كان لديك 1 ميغابايت من الذاكرة ، فإن أعلى عنوان هو 1,048,575 . (بالطبع لديك أكثر من ذلك بكثير.)

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

الدرس السابع المؤشرات  Pointers  AD17665987D944F5984405B7F8305269

الشكل يبين عناوين الذاكرة Memory addresses

• عامل العنوان The Address-of Operator &
يمكنك إيجاد العنوان الذي يشغله متغير باستخدام عامل العنوان & . إليك برنامج قصير ، VARADDR ، يوضح كيفية القيام بذلك :


الكود:
// varaddr.cpp
// addresses of variables
#include <iostream>
using namespace std;

int main()
{
int var1 = 11;       //define and initialize
int var2 = 22;       //three variables
int var3 = 33;

cout << &var1 << endl    //print the addresses
        << &var2 << endl    //of these variables
        << &var3 << endl;
return 0;

يعرّف هذا البرنامج البسيط ثلاثة متغيرات من نوع العدد الصحيح int ، ويقوم بتهيئتها إلى القيم 11 و 22 و 33. ثم يقوم بطباعة عناوين هذه المتغيرات.

تعتمد العناوين الفعلية التي تشغلها المتغيرات في البرنامج على العديد من العوامل ، مثل الكمبيوتر الذي يعمل عليه البرنامج ، وحجم نظام التشغيل ، وما إذا كانت هناك أي برامج أخرى موجودة حاليًا في الذاكرة. لهذه الأسباب ، ربما لن تحصل على نفس العناوين التي حصلنا عليها عند تشغيل هذا البرنامج. (قد لا تحصل على نفس النتائج مرتين على التوالي.) وإليك الإخراج على الجهاز الذى لدينا:

الكود:
 0x8f4ffff4 ← address of var1
0x8f4ffff2 ← address of var2
0x8f4ffff0 ← address of var3

تذكر أن عنوان المتغير لا يشبه محتوياته على الإطلاق. محتويات المتغيرات الثلاثة هي 11 و 22 و 33. ويبين الشكل المتغيرات الثلاثة في الذاكرة.

الدرس السابع المؤشرات  Pointers  A0003F14E1F740B3810CEC3BEEA177D9

الشكل يبين عناوين ومحتويات المتغيرات Addresses and contents of variables

يفسر interprets عامل الإدراج << العناوين في النظام السداسي عشري ، كما هو مبين بالبادئة 0x قبل كل عدد . هذه هي الطريقة المعتادة لإظهار عناوين الذاكرة. إذا لم تكن على دراية بنظام الأرقام السداسية عشرية ، فلا تقلق. كل ما تحتاج إلى معرفته هو أن كل متغير يبدأ في عنوان فريد. ومع ذلك ، قد تلاحظ في الإخراج أن كل عنوان يختلف عن العنوان التالي بمقدار 2 بايت بالضبط. ذلك لأن الأعداد الصحيحة تشغل 2 بايت من الذاكرة (على نظام 16 بت). إذا استخدمنا متغيرات نوع char ، فستكون لديها عناوين متجاورة ، حيث أن char يشغل بايت واحد ؛ وإذا استخدمنا النوع double ، فستختلف العناوين بمقدار 8 بايت.

تظهر العناوين بترتيب تنازلي لأنه يتم تخزين المتغيرات المحلية local على المكدس stack ، والذي ينمو نزولا في الذاكرة. إذا كنا قد استخدمنا المتغيرات العامة global ، فسيكون لها عناوين تصاعدية ، حيث يتم تخزين المتغيرات العامة على الكومة heap ، التي تنمو للأعلى. مرة أخرى ، لا داعي للقلق بشأن هذه الاعتبارات ، لأن المترجم يتتبع التفاصيل نيابة عنك .

لا تخلط بين عامل العنوان & ، الذي يسبق اسم المتغير في إعلان متغير ، مع عامل المرجع & ، الذي يتبع اسم النوع في النموذج الأولي للدالة أو التعريف. (تم مناقشة المراجع فى درس الدوال )

• متغيرات المؤشر Pointer Variables
العناوين في حد ذاتها محدودة إلى حد ما. من الجيد أن نعرف أنه يمكننا معرفة أين توجد الأشياء في الذاكرة ، كما فعلنا في VARADDR ، لكن طباعة قيم العنوان ليست كل الإفادة . تتطلب إمكانية زيادة قوة البرمجة لدينا فكرة إضافية : "المتغيرات التي تحتوي على قيم العنوان" . لقد رأينا أنواعًا متغيرة تخزن الأحرف والأعداد الصحيحة وأرقام الفاصلة العائمة وما إلى ذلك. يتم تخزين العناوين بالمثل. يُطلق على المتغير الذي يحمل قيمة العنوان اسم متغير المؤشر pointer variable ، أو ببساطة المؤشر pointer.

ما هو نوع بيانات متغيرات المؤشر؟ قد تعتقد أن نوع بيانات المؤشر سيطلق عليه شيء مثل pointer أو ptr. ومع ذلك ، فإن الأمور أكثر تعقيدا قليلا. يعرض البرنامج التالي ، PTRVAR ، بناء جملة متغيرات المؤشر.


الكود:
 // ptrvar.cpp
// pointers (address variables)
#include <iostream>
using namespace std;

int main()
{
int var1 = 11;          //two integer variables
int var2 = 22;

cout << &var1 << endl       //print addresses of variables
        << &var2 << endl << endl;

int* ptr;             //pointer to integers
ptr = &var1;             //pointer points to var1

cout << ptr << endl;          //print pointer value

ptr = &var2;             //pointer points to var2
cout << ptr << endl;          //print pointer value
return 0;
}

يعرف هذا البرنامج متغيرين لعددين صحيحين integer variables ، var1 و var2 ، ويقوم بتهيئتهما للقيمتين 11 و 22. ثم يقوم بطباعة عناوينهما.

بعد ذلك يعرّف البرنامج متغير مؤشر pointer variable في السطر :

الكود:
int* ptr;

للمبتدئين هذا بناء جملة غريب إلى حد ما. علامة النجمة (*) تعني المؤشر إلى pointer to . وبالتالي فإن العبارة تعرف المتغير ptr كمؤشر إلى int. هذه طريقة أخرى للقول أن هذا المتغير يمكنه الاحتفاظ بعناوين متغيرات الأعداد الصحيحة.

ما الخطأ في فكرة نوع مؤشر للأغراض العامة يحمل مؤشرات إلى أي نوع بيانات؟ إذا أطلقنا عليه النوع pointer ، فيمكننا كتابة إعلانات مثل

الكود:
pointer ptr;

المشكلة هي أن المترجم يحتاج إلى معرفة نوع المتغير الذي يشير إليه المؤشر. (سنرى لماذا عندما نتحدث عن المؤشرات والمصفوفات .) يسمح بناء الجملة المستخدم في C++ بالإعلان عن أي نوع من المؤشرات:

الكود:
char* cptr;       // pointer to char
int* iptr;       // pointer to int
float* fptr;       // pointer to float
Distance* distptr;    // pointer to user-defined Distance class

وهكذا .

• جدال حول بناء الجملة Syntax
تجدر الإشارة إلى أنه من الشائع كتابة تعريفات المؤشر مع وجود علامة النجمة أقرب إلى اسم المتغير من النوع .

الكود:
char *charptr;

هذا لا يهم المترجم ، لكن وضع علامة النجمة بجوار النوع يساعد في التأكيد على أن علامة النجمة جزء من نوع المتغير (مؤشر إلى حرف pointer to char ) ، وليست جزءًا من الاسم نفسه.

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

الكود:
char* ptr1, * ptr2, * ptr3;    // three variables of type char*

يمكنك استخدام نهج علامة النجمة بجوار الاسم .

الكود:
char *ptr1, *ptr2, *ptr3;    // three variables of type char*

• يجب أن تحتوي المؤشرات على قيمة
يمكن اعتبار عنوان مثل 0x8f4ffff4 كثابت مؤشر pointer constant. يمكن اعتبار مؤشر مثل ptr كمتغير مؤشر pointer variable . مثلما يمكن تعيين متغير العدد الصحيح var1 القيمة الثابتة 11 ، كذلك يمكن تعيين متغير المؤشر ptr القيمة الثابتة 0x8f4ffff4 .

عند تعريف المتغير لأول مرة ، فإنه لا يحمل أي قيمة (ما لم نقم بتهيئته في نفس الوقت). قد يحتوي على قيمة نفايات ، ولكن هذا ليس له معنى. في حالة المؤشرات ، فإن قيمة بيانات النفايات هي عنوان شيء ما في الذاكرة ، ولكن ربما لا يتعلق بأي شيء نريده. لذلك قبل استخدام المؤشر ، يجب وضع عنوان محدد فيه. في برنامج PTRVAR ، يتم تعيين ptr أولاً بعنوان var1 في السطر

الكود:
 ptr = &var1; ← put address of var1 in ptr

بعد ذلك ، يقوم البرنامج بطباعة القيمة الموجودة في ptr ، والتي يجب أن تكون هي نفس العنوان المطبوع &var1 . ثم يتم تعيين نفس المتغير المؤشر ptr بعنوان var2 ، ويتم طباعة هذه القيمة. يوضح الشكل تشغيل برنامج PTRVAR.

الدرس السابع المؤشرات  Pointers  42A0830612B946459DCBC1C4652B7927

الشكل يبين تغيير القيم فى المؤشر ptr
إليك إخراج PTRVAR:

الكود:
0x8f51fff4 ←address of var1
0x8f51fff2 ← address of var2
0x8f51fff4 ← ptr set to address of var1
0x8f51fff2 ← ptr set to address of var2

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

• الوصول إلى المتغير ألمشار إليه Accessing the Variable Pointed To
افترض أننا لا نعرف اسم المتغير ولكننا نعرف عنوانه. هل يمكننا الوصول إلى محتويات المتغير؟ (قد يبدو الأمر وكأنه سوء إدارة لفقدان أسماء المتغيرات ، لكننا سنرى قريبًا أن هناك العديد من المتغيرات التي لا نعرف أسماءها.)
يوجد بناء جملة خاص للوصول إلى قيمة المتغير باستخدام عنوانه بدلاً من اسمه.
إليك مثال على ذلك ، برنامج ، PTRACC ، يوضح كيفية تنفيذه:


الكود:
// ptracc.cpp
// accessing the variable pointed to
#include <iostream>
using namespace std;

int main()
{
int var1 = 11;          //two integer variables
int var2 = 22;

int* ptr;          //pointer to integers

ptr = &var1;          //pointer points to var1
cout << *ptr << endl;    //print contents of pointer (11)

ptr = &var2;          //pointer points to var2
cout << *ptr << endl;    //print contents of pointer (22)
return 0;
}



يشبه هذا البرنامج برنامج PTRVAR ، باستثناء أنه بدلاً من طباعة قيم العنوان في ptr ، فإننا نقوم بطباعة قيمة العدد الصحيح المخزنة في العنوان المخزن في ptr. إليك الإخراج:


الكود:
11
22

التعبير الذي يصل إلى المتغيرات var1 و var2 هو *ptr ، والذي يحدث في كل من جملتي cout.

عند استخدام علامة النجمة (*) أمام اسم متغير ، كما هو الحال في التعبير *ptr ، يطلق عليه عامل dereference (أو أحيانًا عامل indirection ). وهذا يعني "قيمة المتغير المشار إليه بواسطة" . وبالتالي فإن التعبير *ptr يمثل قيمة المتغير الذي يشير إليه ptr. عندما يتم تعيين ptr على عنوان var1 ، يكون التعبير *ptr له القيمة 11 ، لأن var1 هو 11. عندما يتم تغيير ptr إلى عنوان var2 ، يكتسب التعبير *ptr القيمة 22 ، لأن var2 هو 22. اسم آخر بالنسبة لعامل dereference هو عامل contents of ، وهي طريقة أخرى لقول نفس الشيء. يوضح الشكل كيف يبدو هذا.

الدرس السابع المؤشرات  Pointers  286DCBA9457D4326BA770328A507B59F

الشكل يبين الوصول عبر المؤشر Access via pointer

يمكنك استخدام المؤشر ليس فقط لعرض قيمة المتغير ، ولكن أيضًا لتنفيذ أي عملية تقوم بها على المتغير مباشرةً. فيما يلي برنامج ، PTRTO ، يستخدم مؤشرًا لتعيين قيمة لمتغير ، ثم لتعيين هذه القيمة لمتغير آخر:


الكود:
// ptrto.cpp
// other access using pointers
#include <iostream>
using namespace std;

int main()
{
int var1, var2;       //two integer variables
int* ptr;          //pointer to integers

ptr = &var1;          //set pointer to address of var1
*ptr = 37;          //same as var1=37
var2 = *ptr;          //same as var2=var1

cout << var2 << endl;    //verify var2 is 37
return 0;
}

تذكر أن علامة النجمة المستخدمة كعامل dereference لها معنى مختلف عن علامة النجمة المستخدمة في الإعلان عن متغيرات المؤشر. يسبق العامل dereference المتغير ويعني "قيمة المتغير المشار إليه بواسطة". علامة النجمة المستخدمة في الإعلان تعني "المؤشر إلى" .


الكود:
 int* ptr;       //declaration: pointer to int
*ptr = 37;       //indirection: value of variable pointed to by ptr

يسمى استخدام عامل dereference للوصول إلى القيمة المخزنة في عنوان "عنونة غير مباشرة" indirect addressing ، أو في بعض الأحيان "إلغاء المؤشر" dereferencing.
• فيما يلي ملخص لما تعلمناه حتى الآن:

الكود:
int v;       //defines variable v of type int
int* p;    //defines p as a pointer to int
p = &v;    //assigns address of variable v to pointer p
v = 3;       //assigns 3 to v
*p = 3;    //also assigns 3 to v

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

• مؤشر إلى الفراغ Pointer to void
قبل أن ننتقل لرؤية المؤشرات في العمل ، يجب أن نلاحظ خصوصية أنواع بيانات المؤشر.
عادةً ، يجب أن يكون العنوان address الذي تضعه في المؤشر هو نفس نوع المؤشر. لا يمكنك تعيين عنوان متغير float لمؤشر int ، على سبيل المثال:


الكود:
float flovar = 98.6;
int* ptrint = &flovar;    //ERROR: can’t assign float* to int*


ومع ذلك ، هناك استثناء لهذا. يوجد نوع مؤشر للأغراض العامة يمكن أن يشير إلى أي نوع بيانات. وهذا ما يسمى مؤشر للفراغ (خالى) pointer to void ، ويتم تعريفه مثل هذا:

الكود:
void* ptr; //ptr can point to any data type


هذه المؤشرات لها استخدامات متخصصة معينة ، مثل تمرير المؤشرات إلى دوال والتى تعمل بشكل مستقل عن نوع البيانات المشار إليها.

المثال التالي يستخدم مؤشرًا إلى void ويوضح أيضًا أنه إذا كنت لا تستخدم void ، فيجب أن تكون حريصًا على تعيين المؤشرات والعنوان من نفس النوع مثل المؤشر. إليك القائمة PTRVOID:



الكود:
// ptrvoid.cpp
// pointers to type void
#include <iostream>
using namespace std;

int main()
{
int intvar;       //integer variable
float flovar;       //float variable

int* ptrint;       //define pointer to int
float* ptrflo;       //define pointer to float
void* ptrvoid;    //define pointer to void

ptrint = &intvar;    //ok, int* to int*
// ptrint = &flovar;    //error, float* to int*
// ptrflo = &intvar;    //error, int* to float*
ptrflo = &flovar; //   ok, float* to float*

ptrvoid = &intvar;    //ok, int* to void*
ptrvoid = &flovar;    //ok, float* to void*
return 0;
}

يمكنك تعيين عنوان intvar إلى ptrint لأنهما كلاهما int* ، لكن لا يمكنك تعيين عنوان flovar إلى ptrint لأن الأول هو
نوع float* والثاني هو نوع int* . ومع ذلك ، يمكن إعطاء ptrvoid أي قيمة مؤشر ، مثل int* ، لأنها مؤشر إلى void .
[/SIZE]

Admin
Admin

عدد المساهمات : 1194
تاريخ التسجيل : 28/01/2014

https://fathallaabdelaziz.forumarabia.com

الرجوع الى أعلى الصفحة اذهب الى الأسفل

الدرس السابع المؤشرات  Pointers  Empty رد: الدرس السابع المؤشرات Pointers

مُساهمة من طرف Admin الثلاثاء أغسطس 06, 2019 7:29 pm

المؤشرات والمصفوفات Pointers and Arrays
هناك ارتباط وثيق بين المؤشرات والمصفوفات. لقد رأينا كيفية الوصول إلى عناصر المصفوفة. البرنامج التالي ، ARRNOTE ، يوفر مراجعة.


الكود:
 // arrnote.cpp
// array accessed with array notation
#include <iostream>
using namespace std;

int main()
{                   //array
int intarray[5] = { 31, 54, 77, 52, 93 };

for(int j=0; j<5; j++)          //for each element,
cout << intarray[j] << endl;    //print value
return 0;
}

تقوم عبارة cout بطباعة كل عنصر في المصفوفة كل فى دوره. على سبيل المثال ، عندما تكون j هي 3 ، يأخذ التعبير intarray[j] قيمة intarray[3] ويصل إلى عنصر المصفوفة الرابع ، العدد الصحيح 52. إليك خرج ARRNOTE:

الكود:
31
54
77
52
93

والمثير للدهشة أن عناصر المصفوفة يمكن الوصول إليها باستخدام تدوين (بدلالة) المؤشر وكذلك تدوين المصفوفة .
المثال التالي ، PTRNOTE ، يشبه ARRNOTE فيما عدا أنه يستخدم تدوين المؤشر.


الكود:
// ptrnote.cpp
// array accessed with pointer notation
#include <iostream>
using namespace std;

int main()
{                   //array
int intarray[5] = { 31, 54, 77, 52, 93 };

for(int j=0; j<5; j++)          //for each element,
cout << *(intarray+j) << endl;    //print value
return 0;
}


التعبير *(intarray+j) في PTRNOTE له نفس التأثير تمامًا مثل intarray[j] في ARRNOTE ، وخرج البرنامج مماثل. لكن كيف نفسر التعبير *(intarray+j) ؟ افترض أن j هي 3 ، وبالتالي فإن التعبير يكافئ *(intarray+3) . نريد أن يمثل هذا محتويات العنصر الرابع للمصفوفة (52). تذكر أن اسم المصفوفة هو عنوانها. التعبير intarray + j هو بالتالي عنوان مع إضافة شيء إليه. قد تتوقع أن intarray + 3 يؤدي إلى إضافة 3 بايت إلى intarray. لكن هذا لا ينتج النتيجة التي نريدها : intarray هي مصفوفة من الأعداد الصحيحة ، و 3 بايت في هذه المصفوفة هي منتصف العنصر الثاني ، وهي ليست مفيدة للغاية. نريد الحصول على العدد الصحيح الرابع في المصفوفة ، وليس البايت الرابع ، كما هو موضح في الشكل. (هذا الشكل يفترض أعداد صحيحة 2 بايت.)

الدرس السابع المؤشرات  Pointers  175E69DC93D344D0B18FB6B6AEB08E5B

الشكل يبين العد بالأعداد الصحيحة Counting by integers

برنامج مترجم C++ ذكي بما يكفي لأخذ حجم البيانات في الاعتبار عندما ينفذ الحساب في عناوين البيانات. إنه يعرف أن intarray عبارة عن مصفوفة من النوع int لأنه تم إعلانها بهذه الطريقة. لذلك عندما يرى التعبير intarray + 3 ، فإنه يفسره كعنوان للعدد الصحيح الرابع في intarray ، وليس البايت الرابع.
لكننا نريد قيمة عنصر المصفوفة الرابع هذا ، وليس العنوان. لأخذ القيمة ، نستخدم العامل dereference (*). التعبير الناتج ، عندما تكون j هى 3 ، هو *(intarray+3) ، وهو محتوى عنصر المصفوفة الرابع ، أو 52.
الآن رأينا لماذا يجب أن يتضمن إعلان المؤشر نوع المتغير المشار إليه. يحتاج المترجم إلى معرفة ما إذا كان المؤشر هو مؤشر إلى int أو مؤشر إلى double بحيث يمكن إجراء الحساب الصحيح للوصول إلى عناصر المصفوفة فهو يضاعف قيمة الفهرس بمقدار 2 في حالة النوع int ، ولكن يضاعفه في 8 في حالة double.


ثوابت المؤشر ومتغيرات المؤشر Pointer Constants and Pointer Variables
افترض أنه ، بدلاً من إضافة j إلى intarray للدخول إلى عناوين المصفوفة ، أردت استخدام عامل الزيادة. هل يمكن أن تكتب *(intarray++) ؟
الجواب هو لا ، والسبب هو أنه لا يمكنك زيادة ثابت (أو تغييره بالفعل بأي طريقة). التعبير intarray هو العنوان الذي اختاره النظام لوضع المصفوفة الخاص بك ، وستظل على هذا العنوان حتى ينتهي البرنامج . intarray هو مؤشر ثابت.
لا يمكنك قول intarray++ . (في نظام تعدد المهام ، قد تتغير العناوين المتغيرة أثناء تنفيذ البرنامج. قد يتم تبديل البرنامج النشط إلى القرص ثم إعادة تحميله في موقع ذاكرة مختلف. ومع ذلك ، فإن هذه العملية غير مرئية للبرنامج.)
ولكن بينما لا يمكنك زيادة عنوان ، يمكنك زيادة مؤشر يحمل عنوانًا. المثال التالي ، PTRINC ، يوضح كيف:


الكود:
 // ptrinc.cpp
// array accessed with pointer
#include <iostream>
using namespace std;

int main()
{
int intarray[] = { 31, 54, 77, 52, 93 };    //array
int* ptrint;                //pointer to int
ptrint = intarray;             //points to intarray

for(int j=0; j<5; j++)          //for each element,
cout << *(ptrint++) << endl;    //print value
return 0;
}


هنا نحدد مؤشرًا إلى int هو ptrint ونمنحه القيمة intarray ، عنوان المصفوفة . الآن يمكننا الوصول إلى محتويات عناصر المصفوفة بالتعبير

الكود:
 *(ptrint++)

يبدأ المتغير ptrint بنفس قيمة العنوان مثل intarray ، مما يتيح الوصول إلى عنصر المصفوفة الأول ، intarray[0] ، الذي يحتوي على القيمة 31 ، كما كان من قبل. ولكن نظرًا لأن ptrint متغير وليس ثابتًا ، فيمكن زيادته. بعد زيادته ، يشير إلى عنصر المصفوفة الثاني ، intarray[1] . يمثل التعبير *(ptrint++) محتويات عنصر المصفوفة الثانى أو 54. تؤدي الحلقة إلى وصول التعبير إلى كل عنصر من عناصر المصفوفة كل بدوره. خرج PTRINC هو نفسه خرج PTRNOTE.

Admin
Admin

عدد المساهمات : 1194
تاريخ التسجيل : 28/01/2014

https://fathallaabdelaziz.forumarabia.com

الرجوع الى أعلى الصفحة اذهب الى الأسفل

الدرس السابع المؤشرات  Pointers  Empty رد: الدرس السابع المؤشرات Pointers

مُساهمة من طرف Admin الثلاثاء أغسطس 06, 2019 7:50 pm

المؤشرات والدوال Pointers and Functions
في الدوال ، هناك ثلاث طرق لتمرير الوسائط إلى دالة : بواسطة القيمة ، وبواسطة المرجع ، وبواسطة المؤشر. إذا كانت الدالة تهدف إلى تعديل المتغيرات في برنامج الاستدعاء ، لا يمكن تمرير هذه المتغيرات بالقيمة ، لأن الدالة لا تحصل إلا على نسخة من المتغير. ومع ذلك ، يمكن استخدام وسيط مرجعى أو مؤشر في هذا الموقف.

تمرير متغيرات بسيطة Passing Simple Variables
سنراجع أولاً الطريقة التي يتم بها تمرير الوسائط حسب المرجع ، ثم نقوم بمقارنتها بتمرير وسائط المؤشر . يظهر برنامج PASSREF التمرير بالمرجع .


الكود:
// passref.cpp
// arguments passed by reference
#include <iostream>
using namespace std;

int main()
{
void centimize(double&);          //prototype

double var = 10.0;             //var has value of 10 inches

cout << “var = “ << var << “ inches” << endl;

centimize(var);             //change var to centimeters
cout << “var = “ << var << “ centimeters” << endl;
return 0;
}
//--------------------------------------------------------------
void centimize(double& v)
{
v *= 2.54; //v is the same as var
}


هنا نريد تحويل متغير var في main() من بوصة إلى سم. نقوم بتمرير المتغير بواسطة المرجع إلى الدالة centimize() . (تذكر أن العامل & الذى يتبع نوع البيانات double في النموذج الأولي لهذه الدالة يشير إلى أن الوسيط يتم تمريره بالمرجع .) الدالة centimize() تضرب المتغير الأصلي فى 2.54. لاحظ كيف تشير الدالة إلى المتغير. فهى تستخدم ببساطة اسم الوسيط v ؛ v و var هي أسماء مختلفة لنفس الشيء.
بمجرد أن يتم تحويله var إلى سنتيمترات ، يعرض main() النتيجة. إليك إخراج PASSREF:


الكود:
 var = 10 inches
var = 25.4 centimeters


يوضح المثال التالي PASSPTR موقفًا مكافئًا عند استخدام المؤشرات:


الكود:
// passptr.cpp
// arguments passed by pointer
#include <iostream>
using namespace std;

int main()
{
void centimize(double*);       //prototype

double var = 10.0;          //var has value of 10 inches
cout << “var = “ << var << “ inches” << endl;

centimize(&var);          //change var to centimeters
cout << “var = “ << var << “ centimeters” << endl;
return 0;
}
//--------------------------------------------------------------
void centimize(double* ptrd)
{
*ptrd *= 2.54; //*ptrd is the same as var
}


خرج PASSPTR هو نفسه خرج PASSREF.


يتم إعلان الدالة centimize() على أنها تأخذ وسيط يمثل مؤشرًا إلى double :

الكود:
 void centimize(double*) // argument is pointer to double

عندما تقوم main() باستدعاء الدالة ، فإنها تزود عنوان المتغير كوسيط :

الكود:
centimize(&var);

تذكر أن هذا ليس هو المتغير نفسه ، حيث إنه يمرر بالمرجع ، ولكن عنوان المتغير.

نظرًا لأن الدالة centimize() تمرر عنوان ، فيجب أن تستخدم عامل dereference ، * ptrd ، للوصول إلى القيمة المخزنة في هذا العنوان:

الكود:
*ptrd *= 2.54; // multiply the contents of ptrd by 2.54

بالطبع هذا هو نفسه

الكود:
*ptrd = *ptrd * 2.54; // multiply the contents of ptrd by 2.54


حيث تعني علامة النجمة المستقلة عملية الضرب.

نظرًا لأن ptrd يحتوي على عنوان var ، فإن أي شيء يتم إجراؤه على *ptrd يتم فعلًا على var.
يوضح الشكل كيف أن تغيير *ptrd في الدالة يغير var في برنامج الاستدعاء .

الدرس السابع المؤشرات  Pointers  757B8E80A02B4247A1FABA9DD8ADC4FC


الشكل يبين تمرير مؤشر إلى دالة Pointer passed to function


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

Admin
Admin

عدد المساهمات : 1194
تاريخ التسجيل : 28/01/2014

https://fathallaabdelaziz.forumarabia.com

الرجوع الى أعلى الصفحة اذهب الى الأسفل

الرجوع الى أعلى الصفحة

- مواضيع مماثلة

 
صلاحيات هذا المنتدى:
لاتستطيع الرد على المواضيع في هذا المنتدى