المؤشرات Pointers :
منتديات الهندسة الكهربية والإلكترونية والميكاترونكس والكومبيوتر :: الميكروكونترولر PIC والبرجة بلغة السى والمترجم مسكروسى برو :: الميكروكونترولر PIC والبرمجة بلغة السى والمترجم ميكروسى برو
صفحة 1 من اصل 1
المؤشرات Pointers :
المؤشرات Pointers :
نحن نعرف أن "المتغيرات" هى مواقع بالذاكرة ، والتى يمكن الوصول إليها عن طريق "المعرفات" الخاصة بها ، وهى أسمائها ، . بهذه الطريقة ، لا يحتاج البرنامج إلى الاهتمام بالعنوان الفعلى للبيانات فى الذاكرة ، بل ببساطة يستخدم المعرف (الاسم) كلما احتاج الرجوع إلى المتغير .
فى لغة السى ، الذاكرة تشبه "الخلايا المتتابعة" (المتسلسلة) ، كل خلية بحجم "واحد بايت" ، وكل خلية لها عنوان فريد . يتم ترتيب خلايا الذاكرة ذات الواحد بايت هذه بطريقة تسمح للبيانات الممثلة بأكبر من بايت واحد بأن تحتل خلايا ذاكرة لها عناوين متتالية .
بهذه الطريقة ، فإن كل خلية يمكن أن تقع بسهولة فى الذاكرة عن طريق عنواها الفريد . على سبيل المثال ، خلية الذاكرة التى عنوانها (1776) تكون دائما مباشرة بعد الخلية التى عنوانها (1775) ، وتسبق الخلية التى عنوانها (1777) ، ويكون هناك بالضبط "ألف خلية" بعد الخلية التى عنوانها (776) ، و ألف خلية قبل الخلية التى عنوانها (2776) .
عندما يتم إعلان (تعريف) المتغير ، فإنه يتم تخصيص (تعيين) مواقع (أماكن) محددة من الذاكرة المطلوبة لتخزين قيمته ( أى عناوين ذاكرته) . عموما ، برامج لغة السى لا تقرر بالضبط عناوين الذاكرة حيث يتم حفظ متغيراتها . هذه المهمة تترك للمترجم ، ووقت التشغيل . ومع ذلك ، قد يكون من المفيد للبرنامج أن يكون قادرا على الحصول على "عنوان المتغير أثناء وقت التشغيل" من أجل الوصول إلى خلايا البيانات التى تكون عند مكان محدد بالنسبة له .
عامل المرجعية (&) Reference Operator :
نحن نعرف أن "المتغيرات" هى مواقع بالذاكرة ، والتى يمكن الوصول إليها عن طريق "المعرفات" الخاصة بها ، وهى أسمائها ، . بهذه الطريقة ، لا يحتاج البرنامج إلى الاهتمام بالعنوان الفعلى للبيانات فى الذاكرة ، بل ببساطة يستخدم المعرف (الاسم) كلما احتاج الرجوع إلى المتغير .
فى لغة السى ، الذاكرة تشبه "الخلايا المتتابعة" (المتسلسلة) ، كل خلية بحجم "واحد بايت" ، وكل خلية لها عنوان فريد . يتم ترتيب خلايا الذاكرة ذات الواحد بايت هذه بطريقة تسمح للبيانات الممثلة بأكبر من بايت واحد بأن تحتل خلايا ذاكرة لها عناوين متتالية .
بهذه الطريقة ، فإن كل خلية يمكن أن تقع بسهولة فى الذاكرة عن طريق عنواها الفريد . على سبيل المثال ، خلية الذاكرة التى عنوانها (1776) تكون دائما مباشرة بعد الخلية التى عنوانها (1775) ، وتسبق الخلية التى عنوانها (1777) ، ويكون هناك بالضبط "ألف خلية" بعد الخلية التى عنوانها (776) ، و ألف خلية قبل الخلية التى عنوانها (2776) .
عندما يتم إعلان (تعريف) المتغير ، فإنه يتم تخصيص (تعيين) مواقع (أماكن) محددة من الذاكرة المطلوبة لتخزين قيمته ( أى عناوين ذاكرته) . عموما ، برامج لغة السى لا تقرر بالضبط عناوين الذاكرة حيث يتم حفظ متغيراتها . هذه المهمة تترك للمترجم ، ووقت التشغيل . ومع ذلك ، قد يكون من المفيد للبرنامج أن يكون قادرا على الحصول على "عنوان المتغير أثناء وقت التشغيل" من أجل الوصول إلى خلايا البيانات التى تكون عند مكان محدد بالنسبة له .
عامل المرجعية (&) Reference Operator :
رد: المؤشرات Pointers :
عامل المرجعية (&) Reference Operator :
يمكن الحصول على "عنوان المتغير" عن طريق سبق (تقديم) اسم المتغير بعلامة العطف (&) ، والمعروفة باسم "عامل المرجعية" ، والذى يمكن ترجمته حرفيا بأنه "عنوان ال" . على سبيل المثال :
- الكود:
foo = &myvar;
وهذا من شأنه تخصيص "قيمة عنوان المتغير myvar " ( &myvar) إلى "قيمة المتغير foo " (foo) ، عن طريق سبق اسم المتغير myvar بعامل المرجعية &، فإننا لا نخصص محتوى المتغير نفسه إلى foo ، ولكن نخصص عنوانه .
العنوان الفعلى للمتغير بالذاكرة "لا يمكن معرفته قبل وقت التشغيل" ، ولكن دعونا نفترض ، من أجل المساعدة فى توضيح بعض المفاهيم ، أن المتغير myvar قد وضع أثناء وقت التشغيل بعنوان الذاكرة (1776) .
فى هذه الحالة اعتبر قطعة الكود الصغيرة التالية :
- الكود:
myvar = 25;
foo = &myvar;
bar = myvar;
القيم المحتواة بكل متغير بعد تنفيذ هذا الجزء تكون كما هو مبين بالمخطط التالى :
• فى البداية ، بعبارة تخصيص ، خصصنا القيمة (25) إلى المتغير myvar ( أصبحت محتوياته 25 وعنوانه ، الذى فرضناه ، بالذاكرة هو 1776 ) .
• ثم ، بعبارة تخصيص ثانية ، خصصنا قيمة عنوان المتغير myvar إلى المتغير foo ، الذى فرضنا أنه 1776 (أصبحت "محتويات" المتغير foo بالقيمة 1776) .
• وأخيرا ، وبعبارة تخصيص ثالثة ، خصصنا القيمة المحتواة بالمتغير myvar (وهى 25 ) إلى المتغير bar (ليصبح محتواه 25 ) . وهذه هى عبارة التخصيص القياسية التى نعرفها جيدا .
الفرق بين عبارة التخصيص الثانية وعبارة التخصيص الثالثة هو ظهور معامل المرجعية (&) .
"المتغير الذى يخزن عنوان متغير آخر يسمى مؤشر Pointer " ، مثل المتغير foo فى المثال السابق . المؤشرات هى ميزة قوية جدا فى لغة السى . لاحقا سوف نتناول طريقة إعلان واستخدام المؤشرات .
عامل إلغاء المرجعية Dereference ( * ) :
رد: المؤشرات Pointers :
عامل إلغاء المرجعية Dereference ( * ) :
كما رأينا للتو ، المتغير الذى يخزن عنوان متغير آخر يسمى "مؤشر" . يقال أن المؤشرات "تشير إلى" المتغير الذى تقوم بحفظ عنوانه .
الميزة المثيرة للاهتمام فى المؤشرات هى أنه "يمكن استخدامها للوصول إلى المتغير الذى تشير إليه مباشرة" . يتم عمل ذلك عن طريق "سبق اسم المؤشر بعامل إلغاء المرجعية ( * ) . العامل فى حد ذاته يمكن قراءته فى صيغة " القيمة المشار إليها بواسطة" .
لذلك ، وبالمتابعة مع قيم المثال السابق ، فأن العبارة التالية :
- الكود:
baz = *foo;
يمكن أن تقرأ على النحو التالى : "قيمة المتغير baz " تساوى "القيمة المشار إليها بالمتغير foo" ، وسوف تقوم العبارة فعليا بتخصيص القيمة 25 إلى المتغير baz ، حيث أن المتغير foo قيمته 1776 ، والقيمة المشار إليها بواسطة 1776 سوف تكون 25 ( فى أعقاب المثال أعلاه) .
من المهم أن نفرق بوضوح بين أن المتغير foo يشير إلى القيمة 1776 ، فى حين أن (*foo) (مع وجود * تسبق اسم المتغير) تشير إلى القيمة المخزنة عند العنوان 1776 ، وهذه القيمة فى هذه الحالة هى 25 . لاحظ الفرق بين تضمين أو عدم تضمين عامل إلغاء المرجعية كما هو مكتوب بالتعليقات على العبارات التالية :
- الكود:
baz = foo; // baz equal to foo (1776)
baz = *foo; // baz equal to value pointed to by foo (25)
وبالتالى فإن عامل المرجعية وعامل إلغاء المرجعية هى عوامل مكملة أو متتامة :
· العامل & هو عامل المرجعية ، ويمكن قراءته كما يلى "عنوان ال .." .
· العامل * هو عامل إلغاء المرجعية ، ويمكن قراءته كما يلى "القيمة المشار إليها بواسطة ..." .
وبالتالى فإن لها معانى معكوسة : المتغير الذى يرجع إليه بالعامل & يمكن أن يتم إلغاء مرجعيته بالعامل * .
فى وقت سابق أجرينا عملتى التخصيص التالية :
- الكود:
myvar = 25;
foo = &myvar;
بعد هذه العبارات مباشرة ، فإن جميع العبارات التالية تعطى "صواب" true كنتيجة :
- الكود:
myvar == 25
&myvar == 1776
foo == 1776
*foo == 25
· التعبير الأول واضح تماما ، باعتبار أن عملية التخصيص التى أجريت على المتغير myvar كانت myvar = 25 .
· التعبير الثانى يستخدم عامل المرجعية (&) ، والذى يعيد return عنوان المتغير myvar ، والذى فرضنا أن له القيمة 1776 .
· التعبير الثالث واضح إلى حد ما ، لأن التعبير الثانى كان صحيحا وأن عملية التخصيص التى أجريت على المتغير foo كانت foo = &myvar .
· التعبير الرابع يستخدم عامل إلغاء المرجعية (*) والذى يمكن قراءتع بالصيغة "القيمة المشار إليها بواسطة ..." ، والقيمة المشار إليها بالمتغير foo هى فى الواقع 25 .
لذلك ، وبعد كل ذلك ، يمكنك أيضا أن تستنتج أنه طالما أن العنوان المشار إلية بالمتغير foo لم يتغير ، فإن التعبير التالى سوف يكون صواب true أيضا :
- الكود:
*foo == myvar
إعلان (تعريف) المؤشرات Declaring Pointers :
رد: المؤشرات Pointers :
إعلان (تعريف) المؤشرات Declaring Pointers :
نتيجة لقدرة المؤشر على الإشارة (الرجوع) مباشرة إلى القيمة التى يشير إليها ، فإن المؤشر له خصائص مختلفة عندما يشير إلى قيمة لحرف char عنه عندما يشير إلى قيمة لعدد صحيح int أو عدد حقيقى float . بمجرد إلغاء المرجعية ، فإن النوع ينبغى أن يكون معروفا . ولهذا ، فإن "إعلان المؤشر ينبغى أن يتضمن نوع البيانات التى سوف يشير إليها المؤشر .
إعلان المؤشرات يكون بالصيغة التالية :
حيث النوع type هو نوع البيانات المشار إليها بواسطة المؤشر . هذا النوع ليس هو نوع المؤشر نفسه ، ولكن نوع البيانات التى يشير إليها المؤشر . على سبيل المثال :
هذه ثلاثة إعلانات لمؤشرات . كل إعلان يهدف إلى الإشارة إلى نوع مختلف من البيانات ، ولكن ، فى الواقع ، جميعها مؤشرات وجميعها سوف تحتل نفس مقدار المساحة فى الذاكرة ( حجم الذاكرة للمؤشر يعتمد على المنصة حيث يعمل البرنامج ) . وعلى الرغم من ذلك ، فإن البيانات التى تشير إليها لا تحتل نفس مقدار المساحة ولا هى من نفس النوع :
· المؤشر الأول يشير إلى متغير نوع عدد صحيح int .
· المؤشر الثانى يشير إلى متغير نوع حرف char .
· والمؤشر الأخير يشير إلى متغير من النوع double (عدد حقيقى مزدوج) .
لذلك ، على الرغم من أن هذه الأمثلة للمتغيرات الثلاثة جميعها هى مؤشرات ، فإنها الواقع لها أنواع مختلفة من البيانات وهى int , char , double على الترتيب وهذا يتوقف على النوع الذى تشير إليه .
لاحظ أن علامة النجمة (*) تستخدم عندما يعلن عن مؤشر فإنه يعنى فقط أن هذا يكون مؤشرا ( لأن هذا جزء من محدد النوع المركب ) ، وينبغى عدم الخلط بينه وبين "عامل إلغاء المرجعية" الذى تم التعرف عليه من قليل والذى يكتب أيضا كعلامة النجمة (*) . فهما ببساطة شيئين مختلفين ممثلة بنفس العلامة .
مثال على المؤشرات :
نتيجة لقدرة المؤشر على الإشارة (الرجوع) مباشرة إلى القيمة التى يشير إليها ، فإن المؤشر له خصائص مختلفة عندما يشير إلى قيمة لحرف char عنه عندما يشير إلى قيمة لعدد صحيح int أو عدد حقيقى float . بمجرد إلغاء المرجعية ، فإن النوع ينبغى أن يكون معروفا . ولهذا ، فإن "إعلان المؤشر ينبغى أن يتضمن نوع البيانات التى سوف يشير إليها المؤشر .
إعلان المؤشرات يكون بالصيغة التالية :
- الكود:
type * name;
حيث النوع type هو نوع البيانات المشار إليها بواسطة المؤشر . هذا النوع ليس هو نوع المؤشر نفسه ، ولكن نوع البيانات التى يشير إليها المؤشر . على سبيل المثال :
- الكود:
int * number;
char * character;
double * decimals;
هذه ثلاثة إعلانات لمؤشرات . كل إعلان يهدف إلى الإشارة إلى نوع مختلف من البيانات ، ولكن ، فى الواقع ، جميعها مؤشرات وجميعها سوف تحتل نفس مقدار المساحة فى الذاكرة ( حجم الذاكرة للمؤشر يعتمد على المنصة حيث يعمل البرنامج ) . وعلى الرغم من ذلك ، فإن البيانات التى تشير إليها لا تحتل نفس مقدار المساحة ولا هى من نفس النوع :
· المؤشر الأول يشير إلى متغير نوع عدد صحيح int .
· المؤشر الثانى يشير إلى متغير نوع حرف char .
· والمؤشر الأخير يشير إلى متغير من النوع double (عدد حقيقى مزدوج) .
لذلك ، على الرغم من أن هذه الأمثلة للمتغيرات الثلاثة جميعها هى مؤشرات ، فإنها الواقع لها أنواع مختلفة من البيانات وهى int , char , double على الترتيب وهذا يتوقف على النوع الذى تشير إليه .
لاحظ أن علامة النجمة (*) تستخدم عندما يعلن عن مؤشر فإنه يعنى فقط أن هذا يكون مؤشرا ( لأن هذا جزء من محدد النوع المركب ) ، وينبغى عدم الخلط بينه وبين "عامل إلغاء المرجعية" الذى تم التعرف عليه من قليل والذى يكتب أيضا كعلامة النجمة (*) . فهما ببساطة شيئين مختلفين ممثلة بنفس العلامة .
مثال على المؤشرات :
رد: المؤشرات Pointers :
مثال على المؤشرات :
لاحظ أنه على الرغم من أنه لا firstvalue ولا secondvalue قد تم تحديد أى قيمة مباشرة لها فى البرنامج ، إلا أن كليهما انتهيا بتحديد قيمة بطريقة "غير مباشرة" من خلال استخدام المؤشر mypointer . فيما يلى كيف حدث ذلك :
· فى البداية يخصص للمؤشر mypointer عنوان المتغير firstvalue باستخدام عامل المرجعية (&) ، بالصيغة التالية :
· بعد ذلك ، "القيمة المشار إليها بواسطة المؤشر " *mypointer يخصص لها القيمة (10) كما يلى :
ولأنه ، عند هذه اللحظة ، المؤشر mypointer يكون مشيرا إلى موقع الذاكرة (عنوان) للمتغير firstvalue ، فإن هذا فى واقع الأمر يعدل من قيمة المتغير firstvalue .
· ولتوضيح أن المؤشر قد يشير إلى متغيرات مختلفة خلال فترة بقائه (حياته) فى البرنامج ، يتم تكرار العملية السابقة مع المتغير secondvalue وبنفس المؤشر mypointer كما يلى :
مثال آخر مفصل أكثر بعض الشىء :
- الكود:
void main ()
{
int firstvalue, secondvalue;
int * mypointer;
……….
………
mypointer = &firstvalue;
*mypointer = 10;
mypointer = &secondvalue;
*mypointer = 20;
……….
……….
}
لاحظ أنه على الرغم من أنه لا firstvalue ولا secondvalue قد تم تحديد أى قيمة مباشرة لها فى البرنامج ، إلا أن كليهما انتهيا بتحديد قيمة بطريقة "غير مباشرة" من خلال استخدام المؤشر mypointer . فيما يلى كيف حدث ذلك :
· فى البداية يخصص للمؤشر mypointer عنوان المتغير firstvalue باستخدام عامل المرجعية (&) ، بالصيغة التالية :
- الكود:
mypointer = &firstvalue;
· بعد ذلك ، "القيمة المشار إليها بواسطة المؤشر " *mypointer يخصص لها القيمة (10) كما يلى :
- الكود:
*mypointer = 10;
ولأنه ، عند هذه اللحظة ، المؤشر mypointer يكون مشيرا إلى موقع الذاكرة (عنوان) للمتغير firstvalue ، فإن هذا فى واقع الأمر يعدل من قيمة المتغير firstvalue .
· ولتوضيح أن المؤشر قد يشير إلى متغيرات مختلفة خلال فترة بقائه (حياته) فى البرنامج ، يتم تكرار العملية السابقة مع المتغير secondvalue وبنفس المؤشر mypointer كما يلى :
- الكود:
mypointer = &secondvalue;
*mypointer = 20;
مثال آخر مفصل أكثر بعض الشىء :
رد: المؤشرات Pointers :
مثال آخر مفصل أكثر بعض الشىء :
تتضمن كل عملية تخصيص تعليق على كيفية قراءة كل سطر : أى استبدال العامل (&) بواسطة الصيغة "عنون ال ...) ، واستبدال العامل ( * ) بالصيغة "قيمة المشار إليه بواسطة ...." .
لاحظ أن هناك تعبيرات مع المؤشرات p1و p2 ، كلاهما مع أو بدون عامل إلغاء المرجعية ( * ) . معنى التعبيير باستخدام عامل إلغاء المرجعية يختلف تماما عن تلك التى بدونه . عندما يسبق هذا العامل اسم مؤشر ، فإن التعبير يشير إلى القيمة المراد الإشارة إليها ، بينما يظهر اسم المؤشر بدون هذا العامل ، فإنه يشير إلى قيمة المؤشر نفسه ( أى عنوان ما يشير إليه المؤشر) .
شىء آخر قد يلفت انتباهك ، وهو السطر التالى :
هنا ، يتم إعلان المؤشرين المستخدمة فى المثال السابق . ولكن لاحظ أن هناك علامة النجمة ( * ) لكل مؤشر ، وذلك لأن كل منهما له النوع int ( مؤشر إلى متغير نوع العدد الصحيح int ) . وهذا يكون مطلوب نتيجة لقواعد الأسبقية . لاحظ أنه بدلا من ذلك كان الكود بالشكل التالى :
فى الواقع ، p1 سوف يكون من النوع int* ، ولكن p2 سوف يكون من النوع int . المسافات لا تهم على الإطلاق لهذا الغرض . ولكن على أى حال ، ببساطة تذكر أن وضع علامة النجمة لكل مؤشر يكون كافيا لمغظم المستخدمين المهتمين بإعلان مؤشرات متعددة لكل عبارة . أو حتى من الأفضل : استخدام عبارات مختلفة من أجل كل متغير .
المؤشرات والمصفوفات :
- الكود:
void main ()
{
int firstvalue = 5, secondvalue = 15;
int * p1, * p2;
p1 = &firstvalue; // p1 = address of firstvalue
p2 = &secondvalue; // p2 = address of secondvalue
*p1 = 10; // value pointed to by p1 = 10
*p2 = *p1; // value pointed to by p2 = value pointed by p1
p1 = p2; // p1 = p2 (value of pointer is copied)
*p1 = 20; // value pointed by p1 = 20
}
تتضمن كل عملية تخصيص تعليق على كيفية قراءة كل سطر : أى استبدال العامل (&) بواسطة الصيغة "عنون ال ...) ، واستبدال العامل ( * ) بالصيغة "قيمة المشار إليه بواسطة ...." .
لاحظ أن هناك تعبيرات مع المؤشرات p1و p2 ، كلاهما مع أو بدون عامل إلغاء المرجعية ( * ) . معنى التعبيير باستخدام عامل إلغاء المرجعية يختلف تماما عن تلك التى بدونه . عندما يسبق هذا العامل اسم مؤشر ، فإن التعبير يشير إلى القيمة المراد الإشارة إليها ، بينما يظهر اسم المؤشر بدون هذا العامل ، فإنه يشير إلى قيمة المؤشر نفسه ( أى عنوان ما يشير إليه المؤشر) .
شىء آخر قد يلفت انتباهك ، وهو السطر التالى :
- الكود:
int * p1, * p2;
هنا ، يتم إعلان المؤشرين المستخدمة فى المثال السابق . ولكن لاحظ أن هناك علامة النجمة ( * ) لكل مؤشر ، وذلك لأن كل منهما له النوع int ( مؤشر إلى متغير نوع العدد الصحيح int ) . وهذا يكون مطلوب نتيجة لقواعد الأسبقية . لاحظ أنه بدلا من ذلك كان الكود بالشكل التالى :
- الكود:
int * p1, p2;
فى الواقع ، p1 سوف يكون من النوع int* ، ولكن p2 سوف يكون من النوع int . المسافات لا تهم على الإطلاق لهذا الغرض . ولكن على أى حال ، ببساطة تذكر أن وضع علامة النجمة لكل مؤشر يكون كافيا لمغظم المستخدمين المهتمين بإعلان مؤشرات متعددة لكل عبارة . أو حتى من الأفضل : استخدام عبارات مختلفة من أجل كل متغير .
المؤشرات والمصفوفات :
مواضيع مماثلة
» البرمجة بلغة السى – الجزء الخامس – المؤشرات -1- مقدمة المؤشرات Pointers :
» الدرس السابع المؤشرات Pointers
» الدرس السابع المؤشرات Pointers
منتديات الهندسة الكهربية والإلكترونية والميكاترونكس والكومبيوتر :: الميكروكونترولر PIC والبرجة بلغة السى والمترجم مسكروسى برو :: الميكروكونترولر PIC والبرمجة بلغة السى والمترجم ميكروسى برو
صفحة 1 من اصل 1
صلاحيات هذا المنتدى:
لاتستطيع الرد على المواضيع في هذا المنتدى