المؤشر (برمجة الحاسوب)
أعتبر عبارات التخصيص ومتغيرات المؤشر من بين "أثمن كنوز" علوم الحاسوب.

aإلى عنوان الذاكرة المرتبط بمتغير ما b، أي أنه aيحتوي على عنوان الذاكرة 1008 للمتغير b. في هذا الرسم التخطيطي، تستخدم بنية الحوسبة نفس مساحة العناوين ونفس نوع البيانات لكل من المؤشرات وغير المؤشرات؛ ولكن هذا ليس شرطًا.في علوم الحاسوب ، المؤشر هو كائن في العديد من لغات البرمجة يخزن عنوانًا في الذاكرة . قد يكون هذا العنوان لقيمة أخرى موجودة في ذاكرة الحاسوب ، أو في بعض الحالات، لقيمة في مكونات الحاسوب المادية المُرتبطة بالذاكرة . يشير المؤشر إلى موقع في الذاكرة، وتُعرف عملية الحصول على القيمة المخزنة في ذلك الموقع بفك مرجعية المؤشر. على سبيل المثال، يمكن اعتبار رقم الصفحة في فهرس كتاب مؤشرًا إلى الصفحة المقابلة؛ ويتم فك مرجعية هذا المؤشر بالانتقال إلى الصفحة التي تحمل رقم الصفحة المحدد وقراءة النص الموجود فيها. يعتمد الشكل والمحتوى الفعليان لمتغير المؤشر على بنية الحاسوب الأساسية .
يُحسّن استخدام المؤشرات الأداء بشكل ملحوظ في العمليات المتكررة، مثل اجتياز هياكل البيانات القابلة للتكرار (مثل السلاسل النصية ، وجداول البحث ، وجداول التحكم ، والقوائم المتصلة ، وهياكل الأشجار ). وعلى وجه الخصوص، غالبًا ما يكون نسخ المؤشرات والوصول إليها أقل تكلفة بكثير من حيث الوقت والمساحة مقارنةً بنسخ البيانات التي تشير إليها هذه المؤشرات والوصول إليها.
تُستخدم المؤشرات أيضًا لتخزين عناوين نقاط الدخول للبرامج الفرعية المُستدعاة في البرمجة الإجرائية ، وللربط أثناء التشغيل بمكتبات الارتباط الديناميكي (DLLs) . في البرمجة كائنية التوجه ، تُستخدم مؤشرات الدوال لربط الأساليب ، وغالبًا ما يتم ذلك باستخدام جداول الأساليب الافتراضية .
المؤشر هو تطبيق بسيط وملموس لنوع بيانات المرجع الأكثر تجريدًا . تدعم العديد من لغات البرمجة، وخاصة اللغات منخفضة المستوى ، نوعًا من المؤشرات، على الرغم من أن بعضها يفرض قيودًا أكثر على استخدامها من غيرها. في حين أن مصطلح "مؤشر" يُستخدم للإشارة إلى المراجع بشكل عام، إلا أنه ينطبق بشكل أدق على هياكل البيانات التي تسمح واجهتها صراحةً بمعالجة المؤشر (حسابيًا عبرتُستخدم المؤشرات كعناوين ذاكرة، على عكس"ملف تعريف الارتباط السحري"أوميزةأخرى تمنع ذلك.ولأن المؤشرات تسمح بالوصول المحمي وغير المحمي إلىعناوين الذاكرة، فإن استخدامها ينطوي على مخاطر، خاصةً في الحالة الأخيرة. غالبًا ما تُخزَّن المؤشرات الأولية بتنسيق مشابه للأعدادالصحيحة؛ ومع ذلك، فإن محاولة الوصول إلى قيمة مؤشر لا يُمثِّل عنوان ذاكرة صالحًا قد يؤدي إلىتعطل(أو احتوائه على بيانات غير صالحة). وللتخفيف من هذه المشكلة المحتملة، ومن بابأمان النوع، تُعتبر المؤشرات نوعًا منفصلاً مُعَلمًا بنوع البيانات التي تُشير إليها، حتى لو كان التمثيل الأساسي عددًا صحيحًا. كما يمكن اتخاذ تدابير أخرى (مثلالتحقق من الصحةوفحصالحدود) للتأكد من أن متغير المؤشر يحتوي على قيمة تُمثِّل عنوان ذاكرة صالحًا وتقع ضمن النطاق العددي الذي يستطيع المعالج الوصول إليه.
تاريخ
في عام 1955، ابتكرت عالمة الحاسوب السوفيتية الأوكرانية كاترينا يوشتشينكو لغة برمجة العناوين (Address) التي أتاحت إمكانية العنونة غير المباشرة وعناوين أعلى رتبة - على غرار المؤشرات. استُخدمت هذه اللغة على نطاق واسع في الحواسيب السوفيتية . مع ذلك، ظلت غير معروفة خارج الاتحاد السوفيتي، ويُنسب الفضل عادةً إلى هارولد لوسون في اختراع المؤشر عام 1964. [ 2 ] في عام 2000، مُنح لوسون جائزة رائد الحاسوب من معهد مهندسي الكهرباء والإلكترونيات (IEEE ) "لاختراعه متغير المؤشر وإدخاله هذا المفهوم في لغة PL/I، مما وفر لأول مرة إمكانية معالجة القوائم المرتبطة بمرونة في لغة برمجة عامة عالية المستوى". [ 3 ] نُشرت ورقته البحثية الرائدة حول هذه المفاهيم في عدد يونيو 1967 من مجلة CACM بعنوان: معالجة القوائم في PL/I. وفقًا لقاموس أكسفورد الإنجليزي ، ظهرت كلمة "مؤشر" لأول مرة في المطبوعات كمؤشر مكدس في مذكرة فنية صادرة عن شركة تطوير الأنظمة (System Development Corporation) .
الوصف الرسمي
في علوم الحاسوب ، المؤشر هو نوع من أنواع المراجع .
البيانات الأولية (أو ببساطة البيانات الأولية ) هي أي بيانات يمكن قراءتها من ذاكرة الكمبيوتر أو كتابتها إليها باستخدام عملية وصول واحدة إلى الذاكرة (على سبيل المثال، كل من البايت والكلمة هما بيانات أولية).
تُعرَّف مجموعة البيانات ( أو ببساطة المجموعة ) بأنها مجموعة من العناصر الأولية المتجاورة منطقيًا في الذاكرة، والتي تُعتبر مجتمعةً بياناتٍ واحدة (على سبيل المثال، قد تتكون المجموعة من 3 بايتات متجاورة منطقيًا، تمثل قيمها إحداثيات نقطة في الفضاء). عندما تتكون المجموعة بالكامل من نفس نوع العنصر الأولي، يُطلق عليها اسم مصفوفة ؛ بمعنى آخر، يُعد العنصر الأولي للكلمة متعددة البايتات مصفوفة من البايتات، وتستخدم بعض البرامج الكلمات بهذه الطريقة.
في سياق هذه التعريفات، يُعد البايت أصغر وحدة أساسية؛ ويحدد كل عنوان ذاكرة بايتًا مختلفًا. ويُعتبر عنوان الذاكرة للبايت الأول من البيانات هو عنوان الذاكرة (أو عنوان الذاكرة الأساسي ) للبيانات بأكملها.
مؤشر الذاكرة (أو المؤشر فقط ) هو عنصر أساسي، تُستخدم قيمته كعنوان ذاكرة؛ ويُقال إن المؤشر يشير إلى عنوان ذاكرة . ويُقال أيضًا إن المؤشر يشير إلى بيانات [في الذاكرة] عندما تكون قيمة المؤشر هي عنوان ذاكرة تلك البيانات.
بشكل عام، المؤشر هو نوع من أنواع المراجع ، ويُقال إنه يشير إلى بيانات مخزنة في مكان ما في الذاكرة ؛ وللحصول على تلك البيانات، يتم فك مرجعية المؤشر . السمة التي تميز المؤشرات عن أنواع المراجع الأخرى هي أن قيمة المؤشر تُفسَّر على أنها عنوان ذاكرة، وهو مفهوم منخفض المستوى.
تُعدّ المراجع بمثابة مستوى من التوجيه غير المباشر: إذ تُحدد قيمة المؤشر عنوان الذاكرة (أي البيانات) الذي سيُستخدم في العملية الحسابية. ولأن التوجيه غير المباشر جانب أساسي من جوانب الخوارزميات، غالبًا ما تُعبّر المؤشرات عن نفسها كنوع بيانات أساسي في لغات البرمجة ؛ ففي لغات البرمجة ذات الكتابة الثابتة (أو القوية )، يُحدد نوع المؤشر نوع البيانات التي يُشير إليها.
الجذور المعمارية
تُعدّ المؤشرات طبقة تجريدية بسيطة للغاية فوق إمكانيات العنونة التي توفرها معظم البنى الحديثة . في أبسط صورة، يُخصّص عنوان ، أو فهرس رقمي ، لكل وحدة ذاكرة في النظام، حيث تكون الوحدة عادةً إما بايت أو كلمة - اعتمادًا على ما إذا كانت البنية تدعم عنونة البايت أو عنونة الكلمة - مما يحوّل الذاكرة بأكملها فعليًا إلى مصفوفة ضخمة . يوفر النظام أيضًا عملية لاسترجاع القيمة المخزنة في وحدة الذاكرة عند عنوان مُحدد (عادةً باستخدام سجلات الجهاز العامة ).
في الحالة الاعتيادية، يكون حجم المؤشر كافيًا لتخزين عناوين أكثر من عدد وحدات الذاكرة في النظام. وهذا يُتيح إمكانية محاولة البرنامج الوصول إلى عنوان لا يُقابل أي وحدة ذاكرة، إما لعدم كفاية الذاكرة المُثبّتة (أي خارج نطاق الذاكرة المُتاحة) أو لعدم دعم بنية النظام لمثل هذه العناوين. تُسمى الحالة الأولى، في بعض المنصات مثل بنية Intel x86 ، خطأ تجزئة (segfault). أما الحالة الثانية، فهي مُمكنة في التنفيذ الحالي لـ AMD64 ، حيث يبلغ طول المؤشرات 64 بت، بينما لا تتجاوز العناوين 48 بت. يجب أن تلتزم المؤشرات بقواعد مُحددة (العناوين المتعارف عليها)، لذا إذا تم الوصول إلى مؤشر غير متعارف عليه، يُصدر المعالج خطأ حماية عام .
من جهة أخرى، تحتوي بعض الأنظمة على وحدات ذاكرة أكثر من عدد عناوينها. في هذه الحالة، تُستخدم آلية أكثر تعقيدًا، مثل تجزئة الذاكرة أو الترحيل، لاستخدام أجزاء مختلفة من الذاكرة في أوقات مختلفة. تدعم الإصدارات الأخيرة من معمارية x86 ما يصل إلى 36 بتًا من عناوين الذاكرة الفعلية، والتي كانت تُربط بمساحة العناوين الخطية 32 بتًا عبر آلية ترحيل PAE . وبالتالي، لا يمكن الوصول إلا إلى 1/16 من إجمالي الذاكرة المتاحة في كل مرة. مثال آخر في نفس عائلة الحواسيب هو الوضع المحمي 16 بتًا لمعالج 80286 ، الذي، على الرغم من دعمه 16 ميجابايت فقط من الذاكرة الفعلية، كان بإمكانه الوصول إلى ما يصل إلى 1 جيجابايت من الذاكرة الافتراضية، إلا أن الجمع بين عنوان 16 بتًا وسجلات القطاعات جعل الوصول إلى أكثر من 64 كيلوبايت في بنية بيانات واحدة أمرًا شاقًا.
لضمان واجهة متسقة، توفر بعض البنى إدخال/إخراج مُرتبط بالذاكرة ، مما يسمح لبعض العناوين بالإشارة إلى وحدات الذاكرة بينما تشير عناوين أخرى إلى سجلات أجهزة أخرى في الحاسوب. وهناك مفاهيم مماثلة، مثل إزاحات الملفات، ومؤشرات المصفوفات، ومراجع الكائنات البعيدة، التي تؤدي بعض الأغراض نفسها التي تؤديها العناوين لأنواع أخرى من الكائنات.
الاستخدامات
تُدعم المؤشرات بشكل مباشر ودون قيود في لغات مثل PL/I و C و C++ و Pascal و FreeBASIC ، وبشكل ضمني في معظم لغات التجميع . وتُستخدم بشكل أساسي لإنشاء المراجع ، التي بدورها أساسية لبناء جميع هياكل البيانات تقريبًا ، ولنقل البيانات بين أجزاء مختلفة من البرنامج.
في لغات البرمجة الوظيفية التي تعتمد بشكل كبير على القوائم، تُدار مراجع البيانات بشكل مجرد باستخدام بنيات أولية مثل cons والعناصر المقابلة لها car و cdr ، والتي يمكن اعتبارها مؤشرات متخصصة للمكونين الأول والثاني لخلية cons. وهذا ما يُضفي بعضًا من سمات البرمجة الوظيفية المميزة. من خلال هيكلة البيانات في قوائم cons هذه ، تُسهّل هذه اللغات الوسائل التكرارية لبناء البيانات ومعالجتها، على سبيل المثال، من خلال الوصول التكراري إلى عنصري البداية والنهاية لقوائم القوائم؛ مثل "أخذ car من cdr من cdr". في المقابل، تُسهّل إدارة الذاكرة القائمة على فك مرجعية المؤشرات، في شكل تقريبي لمصفوفة من عناوين الذاكرة، التعامل مع المتغيرات كخانات يمكن إسناد البيانات إليها بشكل إلزامي .
عند التعامل مع المصفوفات، تتضمن عملية البحث الأساسية عادةً مرحلة تُسمى حساب العنوان ، والتي تتضمن إنشاء مؤشر إلى عنصر البيانات المطلوب في المصفوفة. أما في هياكل البيانات الأخرى، مثل القوائم المتصلة ، فتُستخدم المؤشرات كمرجع لربط جزء من الهيكل بآخر بشكل صريح.
تُستخدم المؤشرات لتمرير المعاملات بالمرجع. يُفيد هذا إذا أراد المبرمج أن تكون تعديلات الدالة على أحد المعاملات مرئية للمُستدعي. كما يُفيد أيضًا في إرجاع قيم متعددة من دالة.
يمكن استخدام المؤشرات أيضًا لتخصيص وتحرير المتغيرات والمصفوفات الديناميكية في الذاكرة. ولأن المتغير غالبًا ما يصبح زائدًا عن الحاجة بعد انتهاء الغرض منه، فإن الاحتفاظ به يُعدّ إهدارًا للذاكرة، ولذلك يُنصح بتحريره (باستخدام مرجع المؤشر الأصلي) عند انتهاء الحاجة إليه. قد يؤدي عدم القيام بذلك إلى تسرب الذاكرة (حيث تتناقص الذاكرة الحرة المتاحة تدريجيًا، أو بسرعة في الحالات الشديدة، بسبب تراكم العديد من كتل الذاكرة الزائدة).
مؤشرات لغة C
في لغة C ، الصيغة الأساسية لتعريف المؤشر هي: [ 4 ]
int * ptr ;يُعلن هذا عن متغير ptrيخزن مؤشرًا إلى كائن من النوع int. يمكن استخدام أنواع أخرى بدلاً من int؛ على سبيل المثال، bool *ptrسيُعلن عن مؤشر إلى كائن من النوع bool.
بعد تعريف المؤشر، يمكن إسناد عنوان إليه. في لغة C، يمكن استرجاع عنوان المتغير باستخدام &المعامل الأحادي :
// تعريف متغير لمؤشر إلى عدد صحيح int * ptr ; // تعريف متغير لعدد صحيح int a = 5 ; // إسناد عنوان a إلى متغير المؤشر ptr = &a a ؛لإلغاء مرجعية المؤشر، مما ينتج عنه الكائن الذي يشير إليه، *يمكن استخدام علامة النجمة ( ):
printf ( "%d \n " , * ptr ); // يطبع: 5هناك نمطان لإعلان المؤشر intالمسمى ptr:
int*ptr;مما يؤكد أن التعبير*ptrله نوعint. [ 5 ]int*ptr;وهذا يؤكد على أنptrله نوعًاint*. هذا الأسلوب أكثر شيوعًا في لغة C++ منه في لغة C. [ 6 ] [ 7 ]
يُصرّح كل من و بأنه مؤشر إلى ، بينما يُصرّح بأنه . وذلك لأن ينطبق فقط على المتغير الذي يليه مباشرةً. [ 5 ]int*x,y;int*x,y;xintyint*
يمكن أيضًا استخدام علامة النجمة على الجانب الأيسر من عملية التعيين لتغيير الكائن aدون الحاجة إلى أن يكون في نفس النطاق.
* ptr = 8 ;إذا aتم الوصول إليها لاحقًا، فستكون قيمتها الجديدة 8.
لأن لغة C لا تُحدد تهيئة ضمنية للكائنات ذات مدة التخزين التلقائي، [ 8 ] فقد تُشير متغيرات المؤشر أحيانًا إلى مواقع غير متوقعة، مما يُسبب سلوكًا غير مُحدد. ولمعالجة هذه المشكلة، تُهيأ المؤشرات أحيانًا بقيمة مؤشر فارغة ، ممثلة في لغة C بواسطة NULLالماكرو؛ [ 9 ] وفي C23 والإصدارات الأحدث، nullptrيتوفر أيضًا كبديل، وهو من النوع nullptr_t. [ 10 ]
int * ptr = NULL ; int * ptr = nullptr ; // منذ C23يؤدي إلغاء مرجعية مؤشر فارغ إلى سلوك غير محدد ، [ 11 ] مما قد يؤدي إلى أخطاء ونتائج غير متوقعة.
قد يكون هذا المثال أوضح إذا تم فحص الذاكرة مباشرةً. افترض أن المتغير aموجود في العنوان 0x8130 في الذاكرة، وأن ptrالمتغير الآخر موجود في العنوان 0x8134؛ وافترض أيضًا أن هذا جهاز 32 بت، بحيث يكون عرض نوع البيانات int هو 32 بت. فيما يلي ما سيكون موجودًا في الذاكرة بعد تنفيذ مقتطف الشفرة التالي:
int a = 5 ; int * ptr = NULL ;| عنوان | محتويات |
|---|---|
| 0x8130 | 0x00000005 |
| 0x8134 | 0x00000000 |
(المؤشر الفارغ الموضح هنا هو 0x00000000 .) عن طريق تعيين عنوان إلى :aptr
ptr = &a a ؛ينتج عنه قيم الذاكرة التالية:
| عنوان | محتويات |
|---|---|
| 0x8130 | 0x00000005 |
| 0x8134 | 0x00008130 |
ثم عن طريق فك المرجع ptrبالترميز:
* ptr = 8 ;سيقوم الكمبيوتر بأخذ محتويات ptr(وهو 0x8130)، وتحديد موقع ذلك العنوان، وتعيين 8 لذلك الموقع مما ينتج عنه الذاكرة التالية:
| عنوان | محتويات |
|---|---|
| 0x8130 | 0x00000008 |
| 0x8134 | 0x00008130 |
من الواضح أن الوصول إلى القيمة aسيؤدي إلى القيمة 8 لأن التعليمات السابقة عدّلت محتويات aالمؤشر ptr.
الاستخدام في هياكل البيانات
عند إنشاء هياكل بيانات مثل القوائم والطوابير والأشجار، من الضروري استخدام المؤشرات للمساعدة في إدارة كيفية تنفيذ الهيكل والتحكم فيه. من الأمثلة الشائعة على المؤشرات: مؤشرات البداية، ومؤشرات النهاية، ومؤشرات المكدس . يمكن أن تكون هذه المؤشرات مطلقة ( العنوان الفعلي أو عنوان افتراضي في الذاكرة الافتراضية ) أو نسبية ( إزاحة من عنوان بداية مطلق ("أساس")، والتي عادةً ما تستخدم عددًا أقل من البتات مقارنةً بالعنوان الكامل، ولكنها تتطلب عادةً عملية حسابية إضافية واحدة لحلها).
تُعدّ العناوين النسبية شكلاً من أشكال تجزئة الذاكرة اليدوية ، وتتشارك في العديد من مزاياها وعيوبها. يُمكن استخدام إزاحة ثنائية البايت، تحتوي على عدد صحيح غير مُوقّع من 16 بت، لتوفير عناوين نسبية لما يصل إلى 64 كيلوبايت (2^ 16 بايت) من بنية البيانات. يُمكن توسيع هذا بسهولة إلى 128 أو 256 أو 512 كيلوبايت إذا تم إجبار العنوان المُشار إليه على المحاذاة على حدود نصف كلمة أو كلمة أو كلمتين (ولكن يتطلب ذلك عملية إزاحة بتية إضافية إلى اليسار - بمقدار 1 أو 2 أو 3 بتات - لضبط الإزاحة بمعامل 2 أو 4 أو 8، قبل إضافتها إلى العنوان الأساسي ). عمومًا، تُعدّ هذه المخططات مُرهقة، ولتسهيل الأمر على المُبرمج، يُفضّل استخدام العناوين المطلقة (والتي تستند إلى مساحة عناوين مُسطّحة ).
يمكن استخدام إزاحة بايت واحد، مثل قيمة ASCII السداسية العشرية لحرف ما (مثلاً X'29')، للإشارة إلى قيمة عددية بديلة (أو فهرس) في مصفوفة (مثلاً X'01'). وبهذه الطريقة، يمكن ترجمة الأحرف بكفاءة عالية من " البيانات الخام " إلى فهرس تسلسلي قابل للاستخدام ، ثم إلى عنوان مطلق دون الحاجة إلى جدول بحث .
مصفوفات C
في لغة C، يُعرَّف فهرسة المصفوفات رسميًا باستخدام حساب المؤشرات؛ أي أن مواصفات اللغة تشترط أن a[i]يكون التعبير مكافئًا لـ `<sub>1</sub>` *(a + i)، [ 12 ] لأنه إذا aكان `<sub>1</sub>` اسم مصفوفة، فإن التعبير `<sub>1</sub>` aيُقيَّم في معظم السياقات إلى مؤشر إلى العنصر الأول من `<sub>1</sub>` a، [ 13 ] وبالتالي فإن صيغة الوصول إلى المصفوفات هي نفسها التي يمكن استخدامها لفك مرجعية المؤشرات. على سبيل المثال، aيمكن تعريف مصفوفة `<sub>1</sub>` واستخدامها بالطريقة التالية:
int a [ 5 ]; // يُعلن عن 5 أعداد صحيحة متجاورة int * ptr = a ; // تُقيّم أسماء المصفوفات في التعبيرات إلى مؤشرات في معظم الحالات ptr [ 0 ] = 1 ; // يمكن فهرسة المؤشرات باستخدام صيغة المصفوفة * ( a + 1 ) = 2 ; // يمكن فهرسة المصفوفات باستخدام صيغة المؤشر * ( 1 + a ) = 2 ; // جمع المؤشرات تبديلي 2 [ a ] = 4 ; // عامل الفهرسة تبديلي (ربما غير شائع)يُخصّص هذا الأمر كتلةً من خمسة أعداد صحيحة ويُسمّيها `<block>` a؛ ويُقيّم التعبير a، في معظم السياقات، كمؤشر إلى العنصر الأول من الكتلة. ومن الاستخدامات الشائعة الأخرى للمؤشرات الإشارة إلى ذاكرة مُخصّصة ديناميكيًا من دالة `malloc` ، التي تُعيد كتلةً متتاليةً من الذاكرة لا تقلّ عن الحجم المطلوب، ويمكن استخدامها كمصفوفة.
بينما تُقيّم المصفوفة، عند استخدامها كمعامل لمعظم المعاملات، كمؤشر إلى العنصر الأول منها، فإن هذا لا ينطبق على المعامل sizeof. في هذا المثال، sizeof(a)سيُقيّم المعامل إلى 5 * sizeof(int)(حجم المصفوفة)، بينما sizeof(ptr)سيُقيّم المعامل إلى sizeof(int *)، وهو حجم المؤشر نفسه.
يمكن تعريف القيم الافتراضية للمصفوفة على النحو التالي:
int a [ 5 ] = { 2 , 4 , 3 , 1 , 5 };إذا aكان الملف موجودًا في الذاكرة بدءًا من العنوان 0x1000 على جهاز ذي 32 بت بنظام little-endian، فستحتوي الذاكرة على ما يلي (القيم بالنظام الست عشري ، مثل العناوين):
0 1 2 3 1000 2 0 0 0 1004 4 0 0 0 1008 3 0 0 0 100 درجة مئوية 1 0 0 0 1010 5 0 0 0
يتم تمثيلها هنا خمسة أعداد صحيحة: 2، 4، 3، 1، و5. تشغل هذه الأعداد الصحيحة الخمسة 32 بت (4 بايت) لكل منها مع تخزين البايت الأقل أهمية أولاً (هذه بنية وحدة المعالجة المركزية ذات ترتيب البايتات الصغير ) ويتم تخزينها بشكل متسلسل بدءًا من العنوان 0x1000 .
صيغة لغة C مع المؤشرات هي:
aيعني 0x1000 ؛a + 1يعني 0x1004 : "+ 1" يعني إضافة حجم 1 ، وهو 4 بايت؛int*aيعني ذلك إلغاء مرجعية محتوياتa. بالنظر إلى المحتويات كعنوان ذاكرة ( 0x1000 )، ابحث عن القيمة في ذلك الموقع ( 0x0002 )؛a[i]يعني رقم العنصرi، الذي يبدأ من الصفر، والذيaيتم ترجمته إلى*(a + i).
المثال الأخير هو كيفية الوصول إلى محتويات الملف a. إليك شرحًا مفصلاً:
a + iهو موقع الذاكرة للعنصر رقمi،aبدءًا منi = 0؛*(a + i)يأخذ عنوان الذاكرة هذا ويقوم بفك مرجعيته للوصول إلى القيمة.
قائمة مرتبطة بلغة C
فيما يلي مثال على تعريف القائمة المرتبطة في لغة C.
struct LinkedList { void * data ; // بيانات هذا الرابط struct LinkedList * next ; // الرابط التالي؛ NULL إذا لم يكن هناك رابط };هذا التعريف التكراري للمؤشر هو نفسه تعريف التكرار المرجعي من لغة هاسكل :
data LinkedList a = Nil | Cons a ( LinkedList a )أين Nilالقائمة الفارغة، وهل Cons a (LinkedList a)هي خلية cons من النوع aمع رابط آخر من النوع نفسه a؟
مع ذلك، يتم التحقق من نوع التعريف الذي يتضمن المراجع، ولا يستخدم قيم إشارات قد تُسبب التباسًا. لهذا السبب، عادةً ما تُعالج هياكل البيانات في لغة C عبر دوال تغليف ، والتي يتم التحقق من صحتها بدقة.
تمرير العنوان باستخدام المؤشرات
يمكن استخدام المؤشرات لتمرير المتغيرات عبر عناوينها، مما يسمح بتغيير قيمتها. على سبيل المثال، انظر إلى كود C التالي :
// يمكن تغيير نسخة من المتغير الصحيح n داخل الدالة دون التأثير على الكود المُستدعي void passByValue ( int n ) { n = 12 ; }// يتم تمرير مؤشر m بدلاً من ذلك. لا يتم إنشاء نسخة من القيمة التي يشير إليها m. void passByAddress ( int * m ) { * m = 14 ; }int main ( void ) { int x = 3 ;// مرر نسخة من قيمة x كوسيط passByValue ( x ); // تم تغيير القيمة داخل الدالة، لكن x ستظل 3 من الآن فصاعدًا// مرر عنوان x كوسيط passByAddress ( & x ); // تم تغيير قيمة x فعليًا بواسطة الدالة، وهي الآن تساوي 14 هناreturn 0 ; }تخصيص الذاكرة الديناميكي
في بعض البرامج، يعتمد حجم الذاكرة المطلوبة على ما يُدخله المستخدم . في هذه الحالة، يحتاج المبرمج إلى تخصيص الذاكرة ديناميكيًا. ويتم ذلك بتخصيص الذاكرة في الكومة (heap) بدلًا من المكدس (stack )، حيث تُخزَّن المتغيرات عادةً (مع العلم أنه يمكن أيضًا تخزين المتغيرات في سجلات وحدة المعالجة المركزية). لا يُمكن تخصيص الذاكرة ديناميكيًا إلا من خلال المؤشرات، ولا يُمكن إعطاء أسماء لها ، كما هو الحال مع المتغيرات الشائعة .
تُستخدم المؤشرات لتخزين وإدارة عناوين كتل الذاكرة المُخصصة ديناميكيًا . تُستخدم هذه الكتل لتخزين كائنات البيانات أو مصفوفات الكائنات. توفر معظم لغات البرمجة الهيكلية والموجهة للكائنات مساحة من الذاكرة، تُسمى الكومة أو المخزن الحر ، يتم من خلالها تخصيص الكائنات ديناميكيًا.
يوضح مثال كود C أدناه كيفية تخصيص كائنات البنية ديناميكيًا والإشارة إليها. توفر مكتبة C القياسية دالة malloc()لتخصيص كتل الذاكرة من الكومة. تأخذ هذه الدالة حجم الكائن المراد تخصيصه كمعامل، وتعيد مؤشرًا إلى كتلة ذاكرة مُخصصة حديثًا مناسبة لتخزين الكائن، أو مؤشرًا فارغًا في حال فشل التخصيص.
// عنصر مخزون قطع الغيار typedef struct { int id ; // رقم القطعة char * name ; // اسم القطعة float cost ; // التكلفة } Item ;// تخصيص وتهيئة كائن Item جديد Item * makeItem ( const char * name ) { Item * item ;// تخصيص كتلة من الذاكرة لكائن Item جديد item = malloc ( sizeof ( * item )); if ( ! item ) { return NULL ; }// تهيئة عناصر الكائن الجديد memset ( item , 0 , sizeof ( * item )); item -> id = -1 ; item -> name = NULL ; item -> cost = 0.0 ;// احفظ نسخة من الاسم في العنصر الجديد item- > name = malloc ( strlen ( name ) + 1 ); if ( ! item- > name ) { free ( item ); return NULL ; } strcpy ( item- > name , name );// إرجاع كائن العنصر الذي تم إنشاؤه حديثًا return item ; }يوضح الكود أدناه كيفية تحرير كائنات الذاكرة ديناميكيًا، أي إعادتها إلى الكومة أو منطقة التخزين الحرة. توفر مكتبة لغة C القياسية دالة free()لتحرير كتلة ذاكرة تم تخصيصها مسبقًا وإعادتها إلى الكومة.
// تحرير كائن Item void destroyItem ( Item * item ) { // التحقق من مؤشر الكائن إذا كان فارغًا if ( ! item ) { return ; }// تحرير اسم السلسلة المخزنة داخل العنصر إذا كان ( item- > name ) { تحرير ( item- > name )؛ item- > name = NULL ؛ }// تحرير كائن العنصر نفسه free ( item ); }الأجهزة ذات الذاكرة المخصصة
في بعض بنى الحوسبة، يمكن استخدام المؤشرات لمعالجة الذاكرة أو الأجهزة المرتبطة بالذاكرة بشكل مباشر.
يُعدّ تعيين عناوين للمؤشرات أداةً لا غنى عنها عند برمجة المتحكمات الدقيقة . فيما يلي مثال بسيط لتعريف مؤشر من نوع int وتهيئته إلى عنوان سداسي عشري، وفي هذا المثال، الثابت 0x7FFF :
int * hardware_address = ( int * ) 0x7FFF ;في منتصف ثمانينيات القرن الماضي، كان استخدام نظام الإدخال والإخراج الأساسي (BIOS) للوصول إلى إمكانيات الفيديو في أجهزة الكمبيوتر الشخصية بطيئًا. كانت التطبيقات التي تتطلب عرضًا مكثفًا تستخدم عادةً الوصول إلى ذاكرة فيديو CGA مباشرةً عن طريق تحويل الثابت السداسي العشري 0xB8000 إلى مؤشر إلى مصفوفة من 80 قيمة عددية صحيحة غير مُوقّعة من 16 بت. تتكون كل قيمة من رمز ASCII في البايت الأدنى، ولون في البايت الأعلى. وبالتالي، لوضع الحرف ' A ' في الصف 5، العمود 2 باللون الأبيض الساطع على خلفية زرقاء، كان المستخدم يكتب رمزًا برمجيًا مشابهًا لما يلي:
#define VID ((unsigned short (*)[80])0xB8000)void foo ( void ) { VID [ 4 ] [ 1 ] = 0x1F00 | "أ" ؛ }يُستخدم في جداول التحكم
تستخدم جداول التحكم ، التي تُستخدم للتحكم في تدفق البرنامج، المؤشرات بشكل مكثف. تُستخدم هذه المؤشرات، التي عادةً ما تكون مُضمنة في مدخل الجدول، على سبيل المثال، لتحديد نقاط دخول الإجراءات الفرعية المراد تنفيذها، بناءً على شروط مُحددة في نفس مدخل الجدول. مع ذلك، يمكن أن تكون المؤشرات مجرد فهارس لجداول أخرى منفصلة، ولكنها مرتبطة، تتضمن مصفوفة من العناوين الفعلية أو العناوين نفسها (بحسب بنيات لغة البرمجة المُتاحة). كما يمكن استخدامها للإشارة إلى مداخل سابقة في الجدول (كما في معالجة الحلقات) أو لتجاوز بعض مداخل الجدول (كما في عبارة switch أو الخروج المبكر من حلقة). لهذا الغرض الأخير، قد يكون "المؤشر" هو رقم مدخل الجدول نفسه، ويمكن تحويله إلى عنوان فعلي من خلال عمليات حسابية بسيطة.
المؤشرات المكتوبة والتحويل
في العديد من لغات البرمجة، تخضع المؤشرات لقيد إضافي يتمثل في أن الكائن الذي تشير إليه له نوع محدد . على سبيل المثال، قد يُصرَّح عن مؤشر ليشير إلى عدد صحيح ؛ عندها ستحاول اللغة منع المبرمج من توجيهه إلى كائنات ليست أعدادًا صحيحة، مثل الأعداد العشرية ، مما يمنع حدوث بعض الأخطاء.
على سبيل المثال، في كود C التالي:
int * money ; char * bags ;moneyسيكون مؤشرًا لعدد صحيح وسيكون bagsمؤشرًا لحرف.
سيؤدي ما يلي إلى ظهور تحذير من المترجم بشأن "التعيين من نوع مؤشر غير متوافق" في نظام GCC :
الحقائب = نقود ؛لأن moneyو bagsتم الإعلان عنهما بأنواع مختلفة.
لإخفاء تحذير المُصرّف، يجب توضيح عملية الإسناد عن طريق تحويل النوع:
bags = ( char * ) money ;وهذا يعني تحويل مؤشر العدد الصحيح moneyإلى مؤشر حرف وتعيينه إلى bags.
يتطلب مسودة عام 2005 لمعيار C أن تحويل المؤشر المشتق من نوع إلى نوع آخر يجب أن يحافظ على صحة المحاذاة لكلا النوعين (6.3.2.3 المؤشرات، الفقرة 7): [ 14 ]
char * external_buffer = "abcdef" ; int * internal_data ;internal_data = ( int * ) external_buffer ; // سلوك غير محدد إذا "لم يكن المؤشر الناتج محاذيًا بشكل صحيح"في اللغات التي تسمح بحسابات المؤشرات، تأخذ هذه الحسابات حجم النوع في الاعتبار. على سبيل المثال، إضافة عدد صحيح إلى مؤشر ينتج عنه مؤشر آخر يشير إلى عنوان أكبر بمقدار ذلك العدد مضروبًا في حجم النوع. هذا يُسهّل حساب عناوين عناصر مصفوفة من نوع مُحدد، كما هو موضح في مثال مصفوفات لغة C أعلاه. عند تحويل مؤشر من نوع إلى آخر ذي حجم مختلف، يجب على المبرمج توقع اختلاف طريقة حساب المؤشرات. في لغة C، على سبيل المثال، إذا كانت المصفوفة moneyتبدأ من 0x2000 sizeof(int)وحجمها 4 بايت، بينما sizeof(char)حجمها 1 بايت، فإن المؤشر الأول money + 1سيشير إلى 0x2004 ، بينما المؤشر الثاني سيشير إلى 0x2001 . تشمل المخاطر الأخرى للتحويل فقدان البيانات عند كتابة بيانات "واسعة" إلى مواقع "ضيقة" (مثل 0x2000 )، ونتائج غير متوقعة عند إزاحة القيم بتًا ، ومشاكل المقارنة، خاصةً مع القيم الموقعة مقابل القيم غير الموقعة.bags + 1bags[0] = 65537;
على الرغم من أنه من المستحيل عمومًا تحديد التحويلات الآمنة أثناء الترجمة، إلا أن بعض اللغات تخزن معلومات عن أنواع البيانات أثناء التشغيل، والتي يمكن استخدامها للتأكد من صحة هذه التحويلات الخطيرة. بينما تقبل لغات أخرى تقريبًا متحفظًا للتحويلات الآمنة، أو لا تقبل أي تحويلات على الإطلاق.
قيمة المؤشرات
في لغتي C و C++، حتى لو تشابه مؤشران في القيمة، فهذا لا يعني بالضرورة أنهما متكافئان. في هاتين اللغتين و LLVM ، تُفسَّر القاعدة على أنها تعني أن "مجرد إشارة مؤشرين إلى نفس العنوان لا يعني بالضرورة أنهما متساويان بمعنى إمكانية استخدامهما بشكل تبادلي"، ويُشار إلى الفرق بين المؤشرين باسم مصدرهما . [ 15 ] إن تحويل نوع البيانات إلى عدد صحيح، مثل ` uintptr_tint`، مُعرَّفٌ من قِبل التنفيذ، والمقارنة الناتجة عنه لا تُضيف أي معلومات إضافية حول إمكانية استخدام المؤشرين بشكل تبادلي. علاوة على ذلك، فإن أي تحويل إضافي إلى بايتات وإجراء عمليات حسابية سيُربك مُحسِّنات الأداء التي تحاول تتبُّع استخدام المؤشرات، وهي مشكلة لا تزال قيد البحث الأكاديمي. [ 16 ]
جعل المؤشرات أكثر أمانًا
بما أن المؤشر يسمح للبرنامج بمحاولة الوصول إلى كائن قد لا يكون مُعرَّفًا، فقد يكون مصدرًا للعديد من أخطاء البرمجة . مع ذلك، فإن فائدة المؤشرات كبيرة لدرجة أنه يصعب تنفيذ مهام البرمجة بدونها. ونتيجة لذلك، ابتكرت العديد من لغات البرمجة بنيات مصممة لتوفير بعض الميزات المفيدة للمؤشرات دون بعض عيوبها ، والتي تُعرف أحيانًا بمخاطر المؤشرات . في هذا السياق، يُشار إلى المؤشرات التي تُعنون الذاكرة مباشرةً (كما هو مستخدم في هذه المقالة) باسمالمؤشرات الخام أوالمؤشرات المجردة، [ 17 ] على النقيض منالمؤشرات الذكيةأو المتغيرات الأخرى.
تتمثل إحدى المشكلات الرئيسية للمؤشرات في أنه طالما أمكن التعامل معها مباشرةً كرقم، فقد تُشير إلى عناوين غير مستخدمة أو إلى بيانات تُستخدم لأغراض أخرى. تستبدل العديد من لغات البرمجة، بما في ذلك معظم لغات البرمجة الوظيفية ولغات البرمجة الإجرائية الحديثة مثل جافا ، المؤشرات بنوع مرجعي أكثر غموضًا، يُشار إليه عادةً ببساطة باسم " مرجع" ، والذي لا يُمكن استخدامه إلا للإشارة إلى الكائنات ولا يُمكن التعامل معه كأرقام، مما يمنع هذا النوع من الأخطاء. وتُعالج فهرسة المصفوفات كحالة خاصة.
يُطلق على المؤشر الذي لا يحتوي على عنوان مُحدد اسم المؤشر الجامح . أي محاولة لاستخدام مثل هذه المؤشرات غير المُهيأة قد تُسبب سلوكًا غير متوقع، إما لأن القيمة الأولية ليست عنوانًا صالحًا، أو لأن استخدامها قد يُلحق الضرر بأجزاء أخرى من البرنامج. غالبًا ما تكون النتيجة خطأ تجزئة الذاكرة ، أو انتهاكًا لمساحة التخزين ، أو تفرعًا جامحًا (إذا استُخدم كمؤشر دالة أو عنوان تفرع).
في الأنظمة التي تعتمد على تخصيص الذاكرة الصريح، من الممكن إنشاء مؤشر معلق عن طريق تحرير منطقة الذاكرة التي يشير إليها. يُعد هذا النوع من المؤشرات خطيرًا ودقيقًا، لأن منطقة الذاكرة التي تم تحريرها قد تحتوي على نفس البيانات التي كانت تحتويها قبل التحرير، ولكن قد يُعاد تخصيصها لاحقًا وتُستبدل ببيانات أخرى بواسطة شيفرة برمجية غير ذات صلة، دون علم الشيفرة السابقة. تمنع اللغات التي تعتمد على جمع البيانات المهملة هذا النوع من الأخطاء، لأن تحرير الذاكرة يتم تلقائيًا عند عدم وجود أي مراجع في النطاق.
في اللغات التي تُجرّد المؤشرات وعملياتها الحسابية، مثل جافا ، يُمكن استخدام المُكرِّرات ، التي تُعتبر وسيلةً أكثر أمانًا للتكرار على جزء من الذاكرة أو مجموعة دون استخدام الوصول المباشر للمؤشرات. العديد من اللغات، حتى تلك التي تدعم المؤشرات، مثل سي++ وسي# وراست، تحتوي على مُكرِّرات للمجموعات.
استيراد java.util.Iterator ؛ استيراد java.util.List ؛List <String> fruits = List.of ( " Apple" , " Banana" , " Cherry" , " Date " ) ; Iterator <String> iterator = fruits.iterator ( ) ;// استخدم المُكرِّر للتنقل بين عناصر القائمة while ( iterator . hasNext ()) { // احصل على العنصر التالي String fruit = iterator . next (); System . out . println ( fruit ); }في لغة C++، يمكن إعادة تعريف المُكرِّر operator++، على غرار الصيغة التقليدية لزيادة المؤشر في لغة C. يقوم المُكرِّر المُعرَّف حسب الطلب عادةً بإعادة تعريف ثلاثة عوامل تشغيل:
operator==/operator!=(مقارنة المساواة)operator++(زيادة)operator*(إلغاء المرجع)
استيراد std ؛باستخدام std :: vector ;// تُعرّف المجموعات مثل vector نوع مُكرِّر، // مع دالتي begin() و end() vector <int> v { 1 , 2 , 3 , 4 , 5 } ; // ' it ' من النوع vector<int>::iterator // التكرار عبر المجموعة بشكل مشابه لحسابات المؤشرات for ( auto it = v.begin ( ); it != v.end ( ) ; ++ it ) { std :: print ( "{}" , * it ); }تدعم بعض لغات البرمجة، مثل C++ ، المؤشرات الذكية ، التي تستخدم شكلاً بسيطاً من عدّ المراجع للمساعدة في تتبع تخصيص الذاكرة الديناميكية، بالإضافة إلى كونها مرجعاً. في غياب حلقات المراجع، حيث يشير كائن إلى نفسه بشكل غير مباشر عبر سلسلة من المؤشرات الذكية، فإن هذه المؤشرات الذكية تقضي على إمكانية وجود مؤشرات معلقة وتسربات للذاكرة. تدعم سلاسل دلفي عدّ المراجع بشكل أصلي.
تقدم لغة البرمجة Rust مدققًا للاستعارة ، وعمرًا للمؤشرات ، وتحسينًا يعتمد على أنواع الخيارات للمؤشرات الفارغة للقضاء على أخطاء المؤشرات، دون اللجوء إلى جمع البيانات المهملة .
أنواع خاصة من المؤشرات
أنواع محددة حسب القيمة
مؤشر فارغ
يُستخدم المؤشر الفارغ للإشارة إلى أن المؤشر لا يُشير إلى كائن صالح. تُستخدم المؤشرات الفارغة عادةً لتمثيل حالات مثل نهاية قائمة غير معروفة الطول أو فشل تنفيذ إجراء ما؛ ويمكن مقارنة هذا الاستخدام للمؤشرات الفارغة بالأنواع القابلة للتصفير وقيمة Nothing في نوع الخيار .
مؤشر متدلٍ
المؤشر المعلق هو مؤشر لا يشير إلى كائن صالح، وبالتالي قد يتسبب في تعطل البرنامج أو سلوكه بشكل غير متوقع. في لغتي البرمجة باسكال وسي ، قد تشير المؤشرات غير المُهيأة بشكل صحيح إلى عناوين غير متوقعة في الذاكرة.
يوضح المثال البرمجي التالي مؤشرًا معلقًا:
int func ( void ) { char * p1 = malloc ( sizeof ( char )); // قيمة غير مُعرَّفة لمكان ما في الذاكرة الديناميكية char * p2 ; // مؤشر معلق (غير مُهيأ) * p1 = 'a' ; // هذا صحيح، بافتراض أن malloc() لم تُرجع NULL. * p2 = 'b' ; // هذا يستدعي سلوكًا غير مُعرَّف }هنا، p2قد يشير إلى أي مكان في الذاكرة، لذا فإن تنفيذ عملية التعيين *p2 = 'b';قد يؤدي إلى تلف منطقة غير معروفة من الذاكرة أو التسبب في خطأ تجزئة الذاكرة .
الفرع البري
عندما يُستخدم مؤشر كعنوان لنقطة دخول برنامج أو بداية دالة لا تُرجع قيمة ، وتكون إما غير مهيأة أو تالفة، فإذا تم استدعاء دالة أو إجراء قفز إلى هذا العنوان، يُقال إن " تفرعًا جامحًا " قد حدث. بعبارة أخرى، التفرع الجامح هو مؤشر دالة غير مستقر (معلق).
عادةً ما تكون العواقب غير متوقعة، وقد يظهر الخطأ بأشكال مختلفة تبعًا لما إذا كان المؤشر عنوانًا "صالحًا" أم لا، وما إذا كانت هناك (مصادفةً) تعليمة صالحة (رمز العملية) في ذلك العنوان. يُعدّ اكتشاف التفرع غير المقصود من أصعب عمليات تصحيح الأخطاء وأكثرها إحباطًا، إذ قد يكون جزء كبير من الأدلة قد فُقد مسبقًا أو نتيجةً لتنفيذ تعليمة أو أكثر غير مناسبة في موقع التفرع. في حال توفره، يستطيع برنامج محاكاة مجموعة التعليمات عادةً ليس فقط اكتشاف التفرع غير المقصود قبل حدوثه، بل أيضًا توفير سجل كامل أو جزئي لتاريخه.
أنواع محددة حسب البنية
المؤشر النسبي التلقائي
المؤشر النسبي التلقائي هو مؤشر تُفسَّر قيمته على أنها إزاحة من عنوان المؤشر نفسه؛ وبالتالي، إذا كان لهيكل البيانات عنصر مؤشر نسبي تلقائي يشير إلى جزء من هيكل البيانات نفسه، فإنه يمكن نقل هيكل البيانات في الذاكرة دون الحاجة إلى تحديث قيمة المؤشر النسبي التلقائي. [ 18 ]
تستخدم براءة الاختراع المذكورة أيضاً مصطلح " المؤشر النسبي الذاتي" للدلالة على نفس المعنى. ومع ذلك، فقد استُخدم معنى هذا المصطلح بطرق أخرى:
- بمعنى الإزاحة من عنوان بنية ما بدلاً من عنوان المؤشر نفسه؛
- بمعنى مؤشر يحتوي على عنوانه الخاص، والذي يمكن أن يكون مفيدًا لإعادة بناء مجموعة من هياكل البيانات التي تشير إلى بعضها البعض في أي منطقة عشوائية من الذاكرة. [ 19 ]
مؤشر أساسي
المؤشر الأساسي هو مؤشر تكون قيمته إزاحة من قيمة مؤشر آخر. ويمكن استخدامه لتخزين وتحميل كتل البيانات، مع تعيين عنوان بداية الكتلة للمؤشر الأساسي. [ 20 ]
الأنواع المحددة حسب الاستخدام أو نوع البيانات
تعدد الاتجاهات
في بعض لغات البرمجة، يمكن للمؤشر أن يشير إلى مؤشر آخر، مما يتطلب عمليات فك مرجعية متعددة للوصول إلى القيمة الأصلية. ورغم أن كل مستوى من مستويات التوجيه غير المباشر قد يزيد من تكلفة الأداء، إلا أنه ضروري أحيانًا لضمان السلوك الصحيح لهياكل البيانات المعقدة . على سبيل المثال، في لغة C، من الشائع تعريف القائمة المرتبطة بدلالة عنصر يحتوي على مؤشر إلى العنصر التالي في القائمة.
typedef struct LinkedList { int value ; struct LinkedList * next ; } LinkedList ;LinkedList * head = NULL ;تستخدم هذه الطريقة مؤشرًا إلى العنصر الأول في القائمة كبديل للقائمة بأكملها. إذا أُضيفت قيمة جديدة إلى بداية القائمة، headيجب تغيير المؤشر ليشير إلى العنصر الجديد. بما أن وسائط لغة C تُمرر دائمًا بالقيمة، فإن استخدام التوجيه المزدوج يسمح بتنفيذ الإضافة بشكل صحيح، وله أثر جانبي مرغوب فيه يتمثل في التخلص من التعليمات البرمجية الخاصة للتعامل مع عمليات الإضافة في بداية القائمة.
// بالنظر إلى قائمة مرتبة عند *head، قم بإدراج العنصر item في أول // موقع تكون فيه جميع العناصر السابقة لها قيمة أقل أو مساوية. void insert ( LinkedList ** head , LinkedList * item ) { // يشير p إلى مؤشر إلى عنصر LinkedList ** p = head ; while ( * p && ( * p ) -> value < item -> value ) { p = & ( * p ) -> next ; } item -> next = * p ; * p = item ; }// يقوم المستدعي بما يلي: insert ( & head , item );في هذه الحالة، إذا كانت قيمة itemأقل من قيمة head، فسيتم تحديث المتصل headبشكل صحيح إلى عنوان العنصر الجديد.
يوجد مثال أساسي في وسيط argv للدالة الرئيسية في لغة C (و C++) ، والذي يتم تقديمه في النموذج الأولي على أنه char **argv(أو char *argv[]) - وذلك لأن المتغير argvنفسه هو مؤشر إلى مصفوفة من السلاسل (مصفوفة من المصفوفات)، لذا *argvفهو مؤشر إلى السلسلة 0 (بحسب الاصطلاح اسم البرنامج)، وهو **argvالحرف 0 من السلسلة 0.
مؤشر الدالة
في بعض لغات البرمجة، يمكن للمؤشر أن يشير إلى شيفرة قابلة للتنفيذ، أي أنه يمكن أن يشير إلى دالة أو طريقة أو إجراء. ويخزن مؤشر الدالة عنوان الدالة المراد استدعاؤها. ورغم إمكانية استخدام هذه الخاصية لاستدعاء الدوال ديناميكيًا، إلا أنها غالبًا ما تكون أسلوبًا مفضلًا لدى مطوري الفيروسات والبرامج الخبيثة الأخرى.
// دالة ذات مُعاملين صحيحين تُرجع قيمة عددية صحيحة int sum ( int n1 , int n2 ) { return n1 + n2 ; }int main ( void ) { int a = 3 ; int b = 5 ; // مؤشر دالة إلى دالة (int, int) -> int // ويشير إلى دالة الجمع int ( * fp )( int , int ) = & sum ; int x = ( * fp )( a , b ); // يستدعي دالة الجمع بالوسيطين a و b int y = sum ( a , b ); // يستدعي دالة الجمع بالوسيطين a و b }في لغة C++ ، يمكن استخدام دالة لامدا ( دالة مجهولة ) بدلاً من مؤشر الدالة.
استيراد std ؛باستخدام دالة رد الاتصال = void ( * )( int );void process ( int a , int b , Callback cb ) { callback ( a + b ); }void printResult ( int result ) { std :: println ( "النتيجة: {}" , result ); }int main ( int argc , char * argv []) { // باستخدام مؤشر الدالة التقليدي: process ( 1 , 2 , printResult ); // يطبع: "النتيجة: 3"// باستخدام تعبير لامدا: process ( 3 , 4 , []( int result ) -> void { std :: println ( "النتيجة: {}" ); }); // يطبع: "النتيجة: 7" }مؤشر خلفي
في القوائم المرتبطة ثنائياً أو هياكل الشجرة ، يشير مؤشر الرجوع الموجود على عنصر ما إلى العنصر الذي يشير إلى العنصر الحالي. هذه المؤشرات مفيدة للتنقل والتعديل، ولكنها تستهلك مساحة أكبر من الذاكرة.
دعم لغات برمجة متنوعة
آدا
لغة آدا هي لغة ذات كتابة قوية، حيث تكون جميع المؤشرات مُحددة النوع، ولا يُسمح إلا بتحويلات الأنواع الآمنة. تُهيأ جميع المؤشرات افتراضيًا إلى نوع `null` null، وأي محاولة للوصول إلى البيانات من خلال nullمؤشر تُؤدي إلى ظهور استثناء . تُسمى المؤشرات في آدا أنواع الوصول . لم تسمح آدا 83 بإجراء العمليات الحسابية على أنواع الوصول (على الرغم من أن العديد من مُوردي المُترجمات وفروا ذلك كميزة غير قياسية)، لكن آدا 95 تدعم العمليات الحسابية "الآمنة" على أنواع الوصول عبر الحزمة System.Storage_Elements`.
أساسي
كانت العديد من الإصدارات القديمة من لغة BASIC لمنصة Windows تدعم STRPTR()إرجاع عنوان سلسلة نصية، وإرجاع VARPTR()عنوان متغير. كما دعمت Visual BasicOBJPTR() 5 إرجاع عنوان واجهة كائن، ومعامل ADDRESSOF لإرجاع عنوان دالة. جميع هذه المتغيرات من نوع عدد صحيح، لكن قيمها تُعادل قيم أنواع المؤشرات.
تتميز لهجات BASIC الأحدث ، مثل FreeBASIC و BlitzMax ، بتطبيقات شاملة للمؤشرات. في FreeBASIC، تُعامل العمليات الحسابية على مؤشرات ANY (المكافئة لمؤشرات C ) كما لو كان مؤشر ANY بعرض بايت واحد. لا يمكن الوصول إلى مؤشرات ANY مباشرةً، كما هو الحال في C. كذلك، لن يُصدر التحويل بين مؤشرات ANY ومؤشرات أي نوع آخر أي تحذيرات.void *
dim as teger f = 257 dim as any ptr g = @f dim as teger ptr i = g assert ( * i = 257 ) assert ( ( g + 4 ) = ( @f + 1 ) )ج
في لغة C ، المؤشرات عبارة عن متغيرات تخزن عناوين، ويمكن أن تكون قيمتها فارغة (null ). لكل مؤشر نوع يشير إليه، ولكن يمكن التحويل بين أنواع المؤشرات بحرية. مع ذلك، لا يمكن التحويل بين مؤشر دالة ومؤشر كائن. يسمح نوع مؤشر خاص يُسمى "مؤشر فارغ" بالإشارة إلى أي كائن (غير دالة)، ولكنه محدود بعدم إمكانية الوصول إليه مباشرةً. غالبًا ما يمكن التلاعب بالعنوان نفسه مباشرةً عن طريق تحويل المؤشر من وإلى نوع عدد صحيح ذي حجم كافٍ، على الرغم من أن النتائج غير محددة؛ في حين أن معايير C السابقة لم تكن تتضمن نوعًا صحيحًا مضمونًا أن يكون كبيرًا بما يكفي، فإن معيار C99 يُحدد uintptr_tالنوع المُعرّف في C99 <stdint.h>، ولكن ليس من الضروري أن يُوفره التطبيق.
تُقيّد معايير اللغة عمليات الحساب على المؤشرات ، أي القدرة على تعديل عنوان المؤشر المستهدف باستخدام العمليات الحسابية (وكذلك مقارنات المقادير)، بحيث تقتصر على حدود كائن مصفوفة واحد أو ما يليه مباشرةً، وإلا ستؤدي إلى سلوك غير مُعرّف . تؤدي إضافة أو طرح قيمة من المؤشر إلى تحريكه بمقدار مُضاعف لحجم نوع بياناته . على سبيل المثال، إضافة 1 إلى مؤشر يشير إلى قيم عددية صحيحة بحجم 4 بايتات ستزيد عنوان البايت الذي يُشير إليه المؤشر بمقدار 4. يؤدي هذا إلى زيادة المؤشر ليُشير إلى العنصر التالي في مصفوفة متصلة من الأعداد الصحيحة، وهو غالبًا ما يكون النتيجة المقصودة. لا يُمكن إجراء عمليات حسابية على voidالمؤشرات لأن نوع void ليس له حجم، وبالتالي لا يُمكن إضافة قيمة إلى العنوان الذي يُشير إليه، على الرغم من أن مُجمّع gcc وغيره من المُجمّعات تُجري عمليات حسابية على البايتات void *كامتداد غير قياسي، مُعاملةً إياه كما لو كان مؤشرًا char *.
تُتيح حسابات المؤشرات للمبرمج طريقةً واحدةً للتعامل مع الأنواع المختلفة: جمع وطرح عدد العناصر المطلوبة بدلاً من الإزاحة الفعلية بالبايتات. على وجه الخصوص، يُصرّح معيار لغة C صراحةً بأنّ الصيغة `n` a[n]، التي تُمثّل العنصر رقم n في المصفوفة `x` ، تُكافئ الصيغة `n`، التي تُمثّل محتوى العنصر الذي يُشير إليه ` x`. هذا يعني، على سبيل المثال، أنّ كلاً من `n` و` n` يصلان إلى العنصر الرابع في المصفوفة `x` .a*(a + n)a + na[3]3[a]a
على الرغم من قوتها، قد تُشكّل حسابات المؤشرات مصدرًا لأخطاء برمجية . فهي تُربك المبرمجين المبتدئين ، وتُجبرهم على التعامل مع سياقات مختلفة: فقد يكون التعبير حسابيًا عاديًا أو حسابيًا باستخدام المؤشرات، ومن السهل أحيانًا الخلط بينهما. ولذلك، لا تسمح العديد من لغات البرمجة الحديثة عالية المستوى بالوصول المباشر إلى الذاكرة باستخدام العناوين.
يدعم معيار ANSI C المؤشر العام (أو voidالمؤشرvoid * ) كنوع مؤشر عام. voidيمكن للمؤشر تخزين عنوان أي كائن (وليس دالة)، ويتم تحويله ضمنيًا إلى أي نوع مؤشر كائن آخر عند التعيين، ولكن يجب تحويله صراحةً عند إلغاء مرجعيته. قبل معيار ANSI C، استخدم K&Rchar * المؤشر العام لغرض "المؤشر غير المحدد النوع".
int x = 4 ; void * p1 = & x ; int * p2 = p1 ; // تم تحويل void * ضمنيًا إلى int * int a = * p2 ; int b = * ( int * ) p1 ; // عند فك المرجع مباشرةً، لا يوجد تحويل ضمنيلغة سي++
تدعم لغة C++ بشكل كامل مؤشرات C وتحويل أنواع البيانات في C. كما تدعم مجموعة جديدة من عوامل تحويل الأنواع للمساعدة في اكتشاف بعض عمليات التحويل غير المقصودة والخطيرة أثناء الترجمة. وتدعم C++ أيضًا شكلاً آخر من أشكال المراجع، يختلف تمامًا عن المؤشر، ويُسمى ببساطة مرجعًا أو نوع مرجعي .
منذ C++11 ، توفر مكتبة C++ القياسية أيضًا مؤشرات ذكية ، والتي يمكن استخدامها في بعض الحالات كبديل أكثر أمانًا لمؤشرات C البدائية.
std::unique_ptr(يمثل ملكية فريدة لمورد، لا يمكن نسخه ولكن يمكن نقله)std::shared_ptr(يمثل الملكية المشتركة لمورد، ولا يتم حذف المورد إلا بعد تدمير أو إعادة تعيين آخر مؤشر مشترك)std::weak_ptr(لا يمثل ملكية، ويمكنه الإشارة إلى موارد مؤشر مشترك دون زيادة عدد المراجع)std::hazard_pointer(مؤشر أحادي الكتابة ومتعدد القراءة يمكن أن يمتلكه خيط واحد على الأكثر في أي وقت)
std::unique_ptrيحل محل المؤشر الذكي السابق std::auto_ptr ، الذي تم إهماله في C++11 وإزالته في C++17.
على عكس لغة C، لا تسمح لغة C++ بالتحويل الضمني void*إلى أنواع مؤشرات أخرى، حتى في عمليات الإسناد. كان هذا قرارًا تصميميًا لتجنب التحويلات غير المقصودة أو حتى غير الدقيقة، مع أن معظم المترجمات لا تُظهر سوى تحذيرات، لا أخطاء، عند مواجهة تحويلات أخرى.
int x = 4 ; void * p1 = & x ; int * p2 = p1 ; // هذا يفشل: لا يوجد تحويل ضمني من void* int * p3 = reinterpret_cast < int *> ( p1 ); // تحويل C++في لغة C++، لا يوجد void&(مرجع إلى void) ليكمل void*(مؤشر إلى void)، لأن المراجع تتصرف مثل أسماء مستعارة للمتغيرات التي تشير إليها، ولا يمكن أن يكون هناك متغير من نوعه void.
مؤشر إلى عضو
في لغة C++، يمكن تعريف مؤشرات إلى الأعضاء غير الثابتة في فئة ما. إذا كانت الفئة MyClassتحتوي على عضو، T aفإن &MyClass::aالمؤشر `<static>` يشير إلى هذا العضو aمن النوع `<static>` T MyClass::*. يمكن أن يكون هذا العضو كائنًا أو دالة . [ 22 ] يمكن استخدام هذه المؤشرات على الجانب الأيمن من المعاملات `<static>` .*و` ->*<static>` للوصول إلى العضو المقابل.
struct Integer { int value ; [[ nodiscard ]] int get () const noexcept { return value ; } };عدد صحيح s1 ؛ // تم إنشاؤه افتراضيًا، قيمة s1 = 0 عدد صحيح * ps = & s1 ؛int Integer ::* ptr = & Integer :: value ; // مؤشر إلى Integer::value int ( Integer ::* fp )() const = & Integer :: get ; // مؤشر إلى Integer::gets1 . * ptr = 1 ; std :: println ( "{}" , ( s1 . * fp )()); // يطبع 1 ps ->* ptr = 2 ; std :: println ( "{}" , ( ps ->* fp )()); // يطبع 2فيما يلي بعض التصريحات التي تتضمن مؤشرات إلى أعضاء:
class X { // ... };class Y { // ... };char X ::* a ; // مؤشر إلى عضو من نوع char char X ::* b [ 5 ]; // مصفوفة من المؤشرات إلى أعضاء من نوع char char * X ::* c ; // مؤشر إلى مؤشر إلى حرف (أحرف) char X ::** d ; // مؤشر إلى مؤشر إلى عضو من نوع char char ( * e )[ 5 ]; // مؤشر إلى مصفوفة (مصفوفات) من الأحرف char X ::* f (); // دالة تُرجع مؤشرًا إلى عضو من نوع char char Y ::* X ::* g ; // مؤشر إلى مؤشر إلى مؤشر إلى عضو إلى مؤشر إلى حرف (أحرف) char X ::* X ::* h ; // مؤشر إلى مؤشر إلى مؤشر إلى عضو إلى مؤشر إلى حرف (أحرف) char ( X ::* i ())[ 5 ]; // دالة تُرجع مؤشرًا إلى عنصر في مصفوفة من الأحرف char ( X ::* j )() // دالة مؤشر إلى عنصر تُرجع حرفًا char ( X :: * k [ 5 ])(); // مصفوفة من مؤشرات الدوال التي تُرجع حرفًاو ()لها []أولوية أعلى من *. [ 23 ]
سي شارب
في لغة البرمجة C# ، تُدعم المؤشرات إما بتحديد كتل التعليمات البرمجية التي تتضمن مؤشرات باستخدام الكلمة unsafeالمفتاحية `<input>`، أو من خلال System.Runtime.CompilerServicesمساحة الاسم `<input>` للوصول إلى المؤشرات. يتشابه بناء الجملة بشكل أساسي مع لغة C++، ويمكن أن يكون العنوان المُشار إليه ذاكرة مُدارة أو غير مُدارة . مع ذلك، يجب تعريف المؤشرات إلى الذاكرة المُدارة (أي مؤشر إلى كائن مُدار) باستخدام الكلمة fixedالمفتاحية `<input>`، مما يمنع جامع البيانات المهملة من نقل الكائن المُشار إليه كجزء من إدارة الذاكرة أثناء وجود المؤشر ضمن النطاق، وبالتالي الحفاظ على صلاحية عنوان المؤشر.
مع ذلك، يُستثنى من ذلك استخدام IntPtrالبنية، وهي مكافئ مُدار بالذاكرة لـ int*، ولا تتطلب الكلمة unsafeالمفتاحية ولا CompilerServicesالتجميع. غالبًا ما يُعاد هذا النوع عند استخدام طرق من System.Runtime.InteropServices، على سبيل المثال:
باستخدام System ؛ باستخدام System.Runtime.InteropServices ؛// الحصول على 16 بايت من الذاكرة من الذاكرة غير المُدارة للعملية IntPtr pointer = Marshal.AllocHGlobal ( 16 ) ;// قم بتنفيذ إجراء ما باستخدام الذاكرة المخصصة// تحرير الذاكرة المخصصة Marshal.FreeHGlobal ( pointer ) ;يتضمن إطار عمل .NET العديد من الفئات والأساليب في Systemمساحات System.Runtime.InteropServicesالأسماء (مثل Marshalالفئة) التي تحوّل أنواع .NET (على سبيل المثال، System.String) إلى العديد من الأنواع والمؤشرات غير المُدارةLPWSTR (على سبيل المثال، ) أو العكس void*، وذلك للسماح بالتواصل مع التعليمات البرمجية غير المُدارة . وتخضع معظم هذه الأساليب لنفس متطلبات أذونات الأمان الخاصة بالتعليمات البرمجية غير المُدارة، نظرًا لقدرتها على التأثير في أي مكان في الذاكرة.
تسمح لغة C# باستخدام المصفوفات المُخصصة في المكدس في التعليمات البرمجية الآمنة باستخدام System.Span. [ 24 ]
namespace Wikipedia.Examples ;باستخدام النظام ؛public class Example { static void Main ( string [] args ) { int num = 1024 ; unsafe { // تحويل عدد صحيح إلى بايتات عن طريق إنشاء مؤشر بايت byte * p = ( byte * ) & number ; Console . Write ( " The 4 bytes of the integer are: " ); for ( int i = 0 ; i < sizeof ( int ); ++ i ) { Console . Write ( " {0:X2}" , * p ); ++ p ; } Console . WriteLine (); }// يمكن تخصيص المصفوفات في الذاكرة المكدسة إما باستخدام المؤشرات أو Span<T> unsafe { int * numbers = stackalloc int [ 5 ]; } Span < int > numbers = stackalloc int [ 5 ]; } }تحتوي لغة C#، مثل لغتي C و C++، على void*نوع (مؤشر فارغ)، ولكن لا يُنصح باستخدامه إطلاقاً. [ 25 ]
على عكس لغتي C و C++، لا يقبل المترجم علامات النجمة المحاذية لليمين. على سبيل المثال:
int * a , b ; int * a , * b ; // غير مسموح به في لغة C#في لغتي C و C++، سيتم تعريف المتغيرين aكـ int*و bكـ int، ولكن في لغة C# سيتم تعريف كلا المتغيرين كـ int*. [ 25 ]
كوبول
تدعم لغة برمجة كوبول المؤشرات إلى المتغيرات. كائنات البيانات الأولية أو الجماعية (السجلات) المُعلنة داخل LINKAGE SECTIONالبرنامج تعتمد بطبيعتها على المؤشرات، حيث أن الذاكرة الوحيدة المُخصصة داخل البرنامج هي مساحة عنوان عنصر البيانات (عادةً كلمة ذاكرة واحدة). في شفرة المصدر للبرنامج، تُستخدم عناصر البيانات هذه تمامًا مثل أي WORKING-STORAGEمتغير آخر، ولكن يتم الوصول إلى محتوياتها ضمنيًا بشكل غير مباشر من خلال LINKAGEمؤشراتها.
يتم عادةً تخصيص مساحة الذاكرة لكل كائن بيانات يتم الإشارة إليه بشكل ديناميكي باستخدام CALLعبارات خارجية أو عبر بنيات لغة موسعة مضمنة مثل عبارات EXEC CICSأو EXEC SQLعبارات.
توفر الإصدارات الموسعة من لغة كوبول أيضًا متغيرات مؤشر مُعلنة باستخدام USAGEISPOINTERعبارات. يتم تحديد قيم هذه المتغيرات المؤشرة وتعديلها باستخدام SETعبارات SETADDRESS.
توفر بعض الإصدارات الموسعة من لغة COBOL أيضًا PROCEDURE-POINTERمتغيرات قادرة على تخزين عناوين التعليمات البرمجية القابلة للتنفيذ .
PL/I
توفر لغة PL/I دعمًا كاملاً للمؤشرات إلى جميع أنواع البيانات (بما في ذلك المؤشرات إلى الهياكل)، والاستدعاء الذاتي ، وتعدد المهام ، ومعالجة السلاسل النصية، ووظائف مدمجة واسعة النطاق . مثّلت PL/I نقلة نوعية مقارنةً بلغات البرمجة في عصرها. مؤشرات PL/I غير مُحددة النوع، وبالتالي لا يتطلب الأمر تحويلًا عند الوصول إلى المؤشر أو تعيين قيمته. صيغة تعريف المؤشر هي `<pre>` ، والتي تُعرّف مؤشرًا باسم "xxx". تُستخدم المؤشرات مع المتغيرات. يمكن تعريف متغير أساسي باستخدام مُحدد موقع افتراضي (`<pre>` أو بدونه (`<pre>` )، حيث `xxx` هو متغير أساسي، والذي قد يكون متغير عنصر، أو هيكلًا، أو مصفوفة، و`ppp` هو المؤشر الافتراضي). يمكن الوصول إلى هذا المتغير بدون مرجع مؤشر صريح (` <pre>`)، أو باستخدام مرجع صريح إلى مُحدد الموقع الافتراضي (`ppp`)، أو إلى أي مؤشر آخر (`<pre> `).DECLARE xxx POINTER;BASEDDECLARE xxx BASED(ppp);DECLARE xxx BASED;xxx=1;qqq->xxx=1;
لا تُعدّ العمليات الحسابية على المؤشرات جزءًا صريحًا من معيار PL/I، ولكن UNSPECالدالة المدمجة والمتغير الوهمي يسمحان بها، كما تسمح العديد من المترجمات بتعبيرات من هذا الشكل ptr = ptr±expression. يحتوي IBM PL/I أيضًا على دالة مدمجة PTRADDلإجراء هذه العمليات. تُجرى العمليات الحسابية على المؤشرات دائمًا بالبايتات.
تحتوي مُجمّعات IBM Enterprise PL/I على شكل جديد من المؤشرات المكتوبة يُسمى HANDLE.
د
لغة البرمجة D هي لغة مشتقة من لغتي C و C++، وتدعم بشكل كامل مؤشرات C وتحويل أنواع البيانات في C. كما تدعم العمليات الحسابية على المؤشرات، بالإضافة إلى شرائح المؤشرات. ويمكن للمؤشر في D أن يتحول void*ضمنيًا إلى نوع آخر. [ 26 ]
في لغة D، من الممكن استخدام refمرجع، والذي يقوم تلقائيًا بإلغاء المرجع، ويمنع العمليات الحسابية على المؤشرات، ولا يسمح بالهروب من دالة (أي من خلال return).
يمكن اعتبار شريحة المؤشر بمثابة مؤشر ذي طول.
استيراد std.stdio ؛void main () { int [] arr = [ 10 , 20 , 30 , 40 , 50 ]; int * ptr = & arr [ 0 ]; int [] slice = ptr [ 0 .. 3 ]; // تقسيم المصفوفة من الفهرس 0 إلى 3 (باستثناء الفهرس 3) writeln ( "Slice: " , slice ); // الناتج: Slice: [10, 20, 30] }إيفل
تستخدم لغة البرمجة الكائنية Eiffel دلالات القيمة والمراجع دون استخدام حساب المؤشرات. ومع ذلك، فهي توفر فئات المؤشرات. وتوفر هذه الفئات حساب المؤشرات، وتحويل الأنواع، وإدارة الذاكرة الصريحة، والتفاعل مع البرامج غير المبنية على Eiffel، وغيرها من الميزات.
فورتران
قدمت لغة فورتران 90 إمكانية استخدام المؤشرات ذات الأنواع المحددة بدقة. لا تقتصر مؤشرات فورتران على مجرد عنوان ذاكرة بسيط، بل تشمل أيضًا الحدود الدنيا والعليا لأبعاد المصفوفة، والخطوات (على سبيل المثال، لدعم أقسام المصفوفة العشوائية)، وبيانات وصفية أخرى. يُستخدم عامل الربط ( ,) =>لربط POINTERمؤشر بمتغير له TARGETسمة. يمكن أيضًا استخدام عبارة فورتران 90 ALLOCATEلربط مؤشر بكتلة من الذاكرة. على سبيل المثال، يمكن استخدام الكود التالي لتعريف وإنشاء بنية قائمة مرتبطة:
نوع real_list_t حقيقي :: بيانات_العينة ( 100 ) نوع ( real_list_t )، مؤشر :: التالي => فارغ () نهاية النوعنوع ( real_list_t )، الهدف :: my_real_list نوع ( real_list_t )، المؤشر :: real_list_tempreal_list_temp => my_real_list do read ( 1 , iostat = ioerr ) real_list_temp % sample_data if ( ioerr /= 0 ) exit allocate ( real_list_temp % next ) real_list_temp => real_list_temp % next end doيُضيف Fortran-2003 دعمًا لمؤشرات الإجراءات. كما يدعم، كجزء من ميزة التوافق مع لغة C ، وظائف مضمنة لتحويل مؤشرات نمط C إلى مؤشرات Fortran والعكس.
يذهب
تحتوي لغة Go على مؤشرات، تُعرَّف بطريقة مشابهة لمؤشرات C/C++ ولكن مع وضع علامة النجمة (*) أولاً. على سبيل المثال، Tيُكتب المؤشر إلى *T`<input>` على النحو التالي: `<input>`. على عكس لغة C، تحتوي Go على خاصية جمع البيانات المهملة، وتمنع العمليات الحسابية على المؤشرات. لا توجد أنواع مرجعية، كما هو الحال في C++. بعض الأنواع المضمنة، مثل الخرائط والقنوات، مُغلَّفة (أي أنها داخليًا مؤشرات إلى هياكل قابلة للتغيير)، ويتم تهيئتها باستخدام الدالة ` make<input>`. في محاولة لتوحيد بناء الجملة بين المؤشرات وغير المؤشرات، ->تم حذف عامل السهم (<input>): يشير عامل النقطة على المؤشر إلى الحقل أو الأسلوب الخاص بالكائن الذي تم فك مرجعيته. ومع ذلك، يعمل هذا فقط مع مستوى واحد من التوجيه غير المباشر.
جافا
لا يوجد تمثيل صريح للمؤشرات في جافا . بدلاً من ذلك، تُنفَّذ هياكل البيانات الأكثر تعقيدًا، مثل الكائنات والمصفوفات ، باستخدام المراجع . لا توفر اللغة أي عوامل تشغيل صريحة لمعالجة المؤشرات. ومع ذلك، لا يزال من الممكن للبرنامج محاولة الوصول إلى مرجع فارغ (مؤشر فارغ)، مما يؤدي إلى ظهور استثناء java.lang.NullPointerException . يتم استعادة المساحة التي تشغلها كائنات الذاكرة غير المُشار إليها تلقائيًا بواسطة عملية جمع البيانات المهملة أثناء التشغيل. [ 27 ]
توفر لغة جافا الفئات java.lang.ref.WeakReferenceو java.lang.ref.PhantomReference، والتي تقوم على التوالي بتنفيذ المراجع الضعيفة والمراجع الوهمية .
مودولا-2
تُنفَّذ المؤشرات بنفس طريقة تنفيذها في لغة باسكال، وكذلك VARالمعاملات في استدعاءات الإجراءات. تتميز لغة Modula-2 بنظام كتابة أكثر صرامة من باسكال، مع عدد أقل من طرق تجاوز نظام الأنواع. تتضمن بعض إصدارات Modula-2 (مثل Modula-3 ) خاصية جمع البيانات المهملة.
أوبرون
كما هو الحال مع Modula-2، تتوفر المؤشرات. ومع ذلك، لا تزال طرق التحايل على نظام الأنواع أقل، لذا فإن Oberon ومشتقاتها أكثر أمانًا فيما يتعلق بالمؤشرات من Modula-2 ومشتقاتها. وكما هو الحال مع Modula-3 ، يُعدّ جمع البيانات المهملة جزءًا من مواصفات اللغة.
باسكال
على عكس العديد من اللغات التي تدعم المؤشرات، فإن معيار ISO Pascal لا يسمح للمؤشرات إلا بالإشارة إلى متغيرات مجهولة المصدر يتم إنشاؤها ديناميكيًا، ولا يسمح لها بالإشارة إلى متغيرات ثابتة أو محلية قياسية. [ 28 ] كما أنه لا يدعم العمليات الحسابية على المؤشرات. يجب أن يكون للمؤشرات نوع بيانات مرتبط بها، ولا يتوافق المؤشر إلى نوع بيانات معين مع المؤشر إلى نوع بيانات آخر (على سبيل المثال، لا يتوافق المؤشر إلى حرف مع المؤشر إلى عدد صحيح). يساعد هذا في التخلص من مشكلات أمان النوع المتأصلة في تطبيقات المؤشرات الأخرى، وخاصة تلك المستخدمة في PL/I أو C. كما أنه يزيل بعض المخاطر الناجمة عن المؤشرات المعلقة ، ولكن إمكانية تحرير المساحة المشار إليها ديناميكيًا باستخدام disposeالإجراء القياسي (الذي له نفس تأثير freeدالة المكتبة الموجودة في C ) تعني أن خطر المؤشرات المعلقة لم يُقضَ عليه تمامًا. [ 29 ]
مع ذلك، في بعض تطبيقات مُترجمات باسكال (أو مشتقاتها) التجارية والمفتوحة المصدر - مثل Free Pascal ، و Turbo Pascal ، و Object Pascal في Embarcadero Delphi - يُسمح للمؤشر بالإشارة إلى متغيرات ثابتة أو محلية قياسية، ويمكن تحويله من نوع مؤشر إلى آخر. علاوة على ذلك، فإن العمليات الحسابية على المؤشرات غير مقيدة: فإضافة أو طرح قيمة من المؤشر يُحركه بمقدار تلك القيمة من البايتات في أي اتجاه، ولكن استخدام الإجراءات Incالقياسية Decمعه يُحرك المؤشر بمقدار حجم نوع البيانات الذي يُشير إليه. كما يُوفر مؤشر غير مُحدد النوع تحت اسم `<type>` Pointer، وهو متوافق مع أنواع المؤشرات الأخرى.
مؤشرات باسكال
يتم تعريف المؤشرات باستخدام عامل فك المرجع ^. عند استخدامه في تعريف متغير، يكون المؤشر مرجعًا إلى نوع، إما أن يكون جوهريًا (محددًا بواسطة مترجم اللغة) أو معلنًا عنه صراحةً، إما في وحدة أخرى، أو في الوحدة الحالية أو البرنامج الرئيسي.
يبدو تعريف المؤشر كالتالي (لغة باسكال غير حساسة لحالة الأحرف ، لذا يمكن استخدام الأحرف الكبيرة أو الصغيرة في الأنواع والمتغيرات):
يكتبالأشهر = ( يناير ، فبراير ، مارس ، أبريل ، مايو ، يونيو ، يوليو ، أغسطس ، سبتمبر ، أكتوبر ، نوفمبر ، ديسمبر ) ؛calptr = ^ Calendar_Type ;نوع_التقويم = سجلالسابق ، التالي : CalPtr ؛الشهر : أشهر ؛يوم : بايت ؛السنة : عدد صحيح ؛الحدث : سلسلة نصية ؛نهاية ؛متغيرذلك : ^ عدد صحيح ؛NextCalendarالتقويم : ^ calptr ;يبدأجديد ( التقويم )calendar ^. prev := nil ;calendar ^. next := nil ;التقويم ^. الشهر := سبتمبر ;التقويم ^. اليوم := 11 ؛التقويم ^. السنة := 2001 ؛جديد ( ذلك ) ؛That ^ := 1 ;جديد ( التقويم التالي ) ؛nextCalendar ^. prev := Calendar ;nextCalendar ^. Next := NIL ;جديد ( التقويم ) ؛...تخلص ( من ذلك ) ؛تخلص من ( التقويم ) ؛تخلص من ( التقويم التالي ) ؛نهاية ؛يُظهر السطر 2 إنشاء نوع جديد باسم months ، ويُنشئ ثوابت مُسماة لأشهر السنة. يُعرّف السطر 3 مؤشرًا إلى نوع سجل. تُعرّف الأسطر من 4 إلى 10 هذا السجل. تُعرّف الأسطر من 12 إلى 15 متغيرات المؤشر؛ يُعرّف السطر 13 مؤشرًا إلى قيمة عددية ، وتُنشئ الأسطر من 14 إلى 15 قائمة مرتبطة. يُخصّص السطر 18 ذاكرة من خلال الدالة الجديدة ، ويُخزّن عنوان تلك الذاكرة في المتغير calendar . تُهيّئ الأسطر من 19 إلى 23 قائمة مرتبطة. يُظهر السطر 26 تعيين قيمة للذاكرة التي يُشير إليها المؤشر that . تُظهر الأسطر من 28 إلى 31 إنشاء رابط جديد في القائمة المرتبطة. تُحرّر الأسطر من 33 إلى 35 الذاكرة المُخصّصة لتلك المؤشرات.
بيرل
تدعم لغة برمجة بيرل المؤشرات ، وإن كان استخدامها نادرًا، وذلك من خلال دالتي التعبئة والفك. تُستخدم هاتان الدالتان فقط للتفاعلات البسيطة مع مكتبات نظام التشغيل المُجمَّعة. في جميع الحالات الأخرى، تستخدم بيرل المراجع ، وهي مُحدَّدة النوع ولا تسمح بأي شكل من أشكال العمليات الحسابية على المؤشرات. تُستخدم المراجع لإنشاء هياكل بيانات معقدة. [ 31 ]
الصدأ
تستخدم لغة Rust المراجع، وهي مشابهة للمؤشرات ولكنها تضمن الإشارة إلى قيمة صحيحة. يُعدّ التعامل مع المؤشرات الخام أقل شيوعًا، ويُستخدم عادةً في برمجة الأنظمة منخفضة المستوى. لا يمكن الوصول إلى المؤشرات الخام إلا داخل كتلة unsafe. [ 32 ] [ 33 ] [ 34 ] توجد العمليات المتعلقة بالمؤشرات الخام في std::ptr.
في لغة Rust، لا يوجد ثابت مؤشر فارغ. يتم تحديده بدلاً من ذلك بواسطة std::ptr::null().
يحتوي Rust أيضًا على عدة أنواع من المؤشرات الذكية:
std::boxed::Box: مكافئ لـ [ 35 ] في لغة C++std::unique_ptrstd::rc::Rc: يعادل مؤشرًا مشتركًا أحادي الخيوط مع عد المراجعstd::sync::Arc: يعادل مؤشرًا مشتركًا آمنًا للخيوط مع عد المراجع الذريةstd::rc::Weak: مكافئ لـ [ 36 ] في لغة C++std::weak_ptr
Tيُكتب المؤشر على النحو التالي في لغة Rust: *const Tأو *mut T. فيما يلي توضيح للمؤشرات الخام في Rust:
fn main () { let mut num : i32 = 42 ;let r1 = & num as * const i32 ; let r2 = & mut num as * mut i32 ;غير آمن { println! ( "r1 يشير إلى: {}" , * r1 ); println! ( "r2 يشير إلى: {}" , * r2 );r2 = 100 ; println! ( " num is now: {}" , num ); } }انظر أيضاً
ملحوظات
مراجع
- ↑ كنوت، دونالد (1974). "البرمجة المهيكلة باستخدام عبارات الانتقال" (ملف PDF) . مجلة Computing Surveys . 6 (5): 261–301 . CiteSeerX 10.1.1.103.6084 . doi : 10.1145/356635.356640 . S2CID 207630080. مؤرشف من النسخة الأصلية (ملف PDF) بتاريخ 24 أغسطس 2009.
- ↑ رايلي ، إدوين د. (2003). معالم بارزة في علوم الحاسوب وتكنولوجيا المعلومات . مجموعة غرينوود للنشر. ص 204. ISBN 9781573565219تم الاطلاع عليه بتاريخ 13 أبريل 2018. مؤشر
هارولد لوسون.
- ↑ "قائمة جوائز جمعية مهندسي الكهرباء والإلكترونيات (IEEE) للحاسبات" . Awards.computer.org. مؤرشف من الأصل بتاريخ 22 مارس 2011. تم الاطلاع عليه بتاريخ 13 أبريل 2018 .
- ↑ ISO/IEC 9899 ، البند 6.7.5.1، الفقرة 1.
- 1 2 ريتشي، دينيس م. (1993-03-01). "تطوير لغة C" . إشعارات SIGPLAN . 28 (3). ACM : 201-208 . doi : 10.1145/155360.155580 .
- ^ بيارن ستروستروب (26 فبراير 2022). "Stroustrup: الأسئلة الشائعة حول أسلوب وتقنية C++" . ستروستروب.كوم . بيارن ستروستروب.
- ↑ ستروستروب، بيارن (2013). "المؤشرات والمصفوفات والمراجع". لغة البرمجة سي++ ( الطبعة الرابعة). أديسون-ويسلي. ص 172. ISBN 978-0-321-56384-2.
- ↑ ISO/IEC 9899 ، البند 6.7.8، الفقرة 10.
- ↑ ISO/IEC 9899 ، البند 7.17، الفقرة 3: NULL... والذي يتوسع إلى ثابت مؤشر فارغ محدد من قبل التنفيذ...
- ↑ غوستيدت، ينس؛ مينيد، جان هيد (22 يوليو 2022). "تقديم ثابت nullptr" . تم الاسترجاع في 13 يوليو 2025 .
- ↑ ISO/IEC 9899 ، البند 6.5.3.2، الفقرة 4، الحاشية 87: إذا تم تعيين قيمة غير صالحة للمؤشر، فإن سلوك المعامل الأحادي
*غير محدد... من بين القيم غير الصالحة لفك مرجعية مؤشر بواسطة*المعامل الأحادي، المؤشر الفارغ... - ↑ بلاوجر، بي جيه ؛ برودي ، جيم (1992). مرجع مبرمج لغة سي وفقًا لمعايير ANSI وISO . ريدموند، واشنطن: مطبعة مايكروسوفت. ص 108. ISBN 978-1-55615-359-4.
- ↑ " الفقرة 6.3.2.1: القيم المنطقية، والمصفوفات، ومعرّفات الدوال". WG14-N3096: مسودة C23: ISO/IEC 9899:2023 (PDF) . ISO / IEC . 1 أبريل 2023. ص 48. مؤرشفة (PDF) من الأصل في 2 أبريل 2023.
- ↑ WG14 N1124 ، C – المعايير المعتمدة: ISO/IEC 9899 – لغات البرمجة – C ، 2005-05-06.
- ↑ يونغ، رالف. "المؤشرات معقدة II، أو: نحن بحاجة إلى مواصفات لغوية أفضل" .
- ↑ يونغ، رالف. "المؤشرات معقدة، أو: ما الذي يوجد في البايت؟" .
- ↑ "شفرة مصدر رأس JVM" . GitHub .
- ↑ براءة اختراع أمريكية رقم 6625718 ، ستاينر، روبرت سي (برومفيلد، كولورادو)، "مؤشرات نسبية لمواقعها الحالية"، صدرت في 23-09-2003، ومخصصة لشركة Avaya Technology Corp. (باسكينج ريدج، نيوجيرسي).
- ↑ براءة اختراع أمريكية رقم 6115721 ، ناجي، مايكل (تامبا، فلوريدا)، "نظام وطريقة لحفظ واستعادة قاعدة البيانات باستخدام المؤشرات الذاتية"، صدرت في 2000-09-05، وتم تخصيصها لشركة IBM (أرمونك، نيويورك).
- ↑ "المؤشرات المستندة (C++)" . Msdn.microsoft.com . تم الاطلاع عليه بتاريخ 13 أبريل 2018 .
- ↑ "التحويل بين مؤشرات الدوال ومؤشرات الكائنات" . CWG (195) . تم الاسترجاع في 15 فبراير 2024 – عبر cplusplus.github.io.
- ↑ "مؤشرات إلى الدوال الأعضاء" . الأسئلة الشائعة حول لغة C++ . مؤسسة C++ القياسية . تم الاطلاع عليه بتاريخ 26-11-2022 .
- ^ بيلتنج، أولف؛ سكانشولم، يناير (2000). Vägen until C [ الطريق إلى C ] (باللغة السويدية) ( الطبعة الثالثة). أدب الطلاب. ص. 169. ردمك 91-44-01468-6.
- ↑ "تعبير تخصيص المكدس" . learn.microsoft.com . مايكروسوفت ليرن. 10 يوليو 2024.
- 1 2 "التعليمات البرمجية غير الآمنة، وأنواع المؤشرات، ومؤشرات الدوال" . learn.microsoft.com . مايكروسوفت ليرن. 6 فبراير 2025.
- ↑ "الأنواع - لغة البرمجة D" . dlang.org . وثائق لغة D. تم الاطلاع عليه في 2 نوفمبر 2025 .
- ↑ نيك بارلانتي،، مكتبة ستانفورد لتعليم علوم الحاسوب ، الصفحات 9-10 (2000).
- ↑ معيار باسكال ISO 7185 (نسخة غير رسمية)، القسم 6.4.4 أنواع المؤشرات. مؤرشف بتاريخ 2017-04-24 في Wayback Machine وما يليه.
- ↑ J. Welsh, WJ Sneeringer, and CAR Hoare, “Ambiguities and Insecurities in Pascal,” Software: Practice and Experience 7 , pp. 685–696 (1977)
- ↑ دليل مرجعي للغة Free Pascal، القسم 3.4 المؤشرات
- ↑ بيانات الاتصال. "// إنشاء المراجع (مراجع بيرل وهياكل البيانات المتداخلة)" . Perldoc.perl.org . تم الاطلاع عليه بتاريخ 13 أبريل 2018 .
- ↑ كلابنيك، ستيف؛ نيكولز، كارول (2023). "المراجع والاقتراض" . لغة برمجة رست ( الطبعة الثانية). دار نشر نو ستارش. ISBN 978-1-7185-0310-6. OCLC 1363816350 .
- ↑ كلابنيك، ستيف؛ نيكولز، كارول (2023). "راست غير الآمن" . لغة برمجة راست ( الطبعة الثانية). دار نشر نو ستارش. ISBN 978-1-7185-0310-6. OCLC 1363816350 .
- ↑ "مؤشر - Rust" . doc.rust-lang.org . تم الاطلاع عليه بتاريخ 15-12-2025 .
- ↑ "Box in std::boxed" . doc.rust-lang.org . 4 أغسطس 2025.
- ↑ "ضعيف في std::rc" . doc.rust-lang.org . 4 أغسطس 2025.
روابط خارجية
- ورقة معالجة قائمة PL/I من عدد يونيو 1967 من مجلة CACM
- cdecl.org أداة لتحويل تعريفات المؤشرات إلى لغة إنجليزية بسيطة
- موقع Over IQ.com: دليل للمبتدئين يشرح المؤشرات بلغة إنجليزية بسيطة.
- مقدمة عن المؤشرات والذاكرة - مكتبة ستانفورد لتعليم علوم الحاسوب
- المؤشرات في برمجة لغة C (مؤرشف بتاريخ 9 يونيو 2019 في أرشيف الإنترنت) نموذج مرئي لمبرمجي لغة C المبتدئين
- 0pointer.de قائمة مختصرة لأقل عدد ممكن من أكواد المصدر التي تُستخدم لفك مرجعية مؤشر فارغ في العديد من لغات البرمجة المختلفة
- كتاب "لغة سي" - يحتوي على أمثلة للمؤشرات بلغة سي ANSI
- اللجنة الفنية المشتركة ISO/IEC JTC 1، اللجنة الفرعية SC 22، فريق العمل WG 14 (2007-09-08). المعيار الدولي ISO/IEC 9899 (ملف PDF) .
{{cite book}}: صيانة CS1: أسماء متعددة: قائمة المؤلفين ( رابط ) صيانة CS1: أسماء رقمية: قائمة المؤلفين ( رابط ) مسودة اللجنة.
- المؤشرات (برمجة الحاسوب)
- الأنواع الأولية
- الاختراعات الأمريكية
- مقارنات لغات البرمجة
