تعريف - شرح المؤشرات في c++




لماذا استخدام المؤشرات؟ (12)

أعلم أن هذا سؤال أساسي حقًا ، لكني بدأت للتو ببعض البرمجة الأساسية لـ C ++ بعد ترميز بعض المشروعات بلغات عالية المستوى.

أساسا لدي ثلاثة أسئلة:

  1. لماذا تستخدم المؤشرات على المتغيرات العادية؟
  2. متى وأين يمكنني استخدام المؤشرات؟
  3. كيف تستخدم المؤشرات مع المصفوفات؟

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

هنا مثال في C:

char hello[] = "hello";

char *p = hello;

while (*p)
{
    *p += 1; // increase the character by one

    p += 1; // move to the next spot
}

printf(hello);

مطبوعات

ifmmp

لأنه يأخذ قيمة لكل حرف ويزيده بمقدار واحد.


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

مرة أخرى في الأيام القديمة من DOS ، استخدمته لتكون قادراً على الوصول إلى ذاكرة الفيديو بطاقة الفيديو مباشرة عن طريق إعلان مؤشر إلى:

unsigned char *pVideoMemory = (unsigned char *)0xA0000000;

العديد من الأجهزة المدمجة لا تزال تستخدم هذه التقنية.


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


اسمحوا لي أن أحاول والإجابة على هذا أيضا.

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

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

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

الآن ماذا يحدث إذا كنت ترغب في تعديل القيمة الأصلية؟ يمكنك استخدام شيء كهذا:

MyType a; //let's ignore what MyType actually is right now.
a = modify(a); 

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

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

MyType *p = NULL; //empty pointer
if(p)
{
    //we never reach here, because the pointer points to nothing
}
//now, let's allocate some memory
p = new MyType[50000];
if(p) //if the memory was allocated, this test will pass
{
    //we can do something with our allocated array
    for(size_t i=0; i!=50000; i++)
    {
        MyType &v = *(p+i); //get a reference to the ith object
        //do something with it
        //...
    }
    delete[] p; //we're done. de-allocate the memory
}

هذا هو المفتاح لماذا تستخدم المؤشرات - تفترض المراجع أن العنصر الذي تشير إليه موجود بالفعل . مؤشر لا.

السبب الآخر الذي يجعلك تستخدم المؤشرات (أو على الأقل في نهاية المطاف الاضطرار إلى التعامل معها) هو أنها نوع بيانات موجود قبل المراجع. لذلك ، إذا انتهيت من استخدام المكتبات للقيام بالأشياء التي تعرف أنها أفضل منها ، ستجد أن الكثير من هذه المكتبات تستخدم المؤشرات في كل مكان ، وذلك ببساطة بسبب طول مدة وجودها (الكثير منهم كتب قبل C ++).

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

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

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

فقط تذكر هذا الاختلاف: المرجع هو في الأساس مؤشر صالح. المؤشر ليس دائمًا صالحًا.

لذلك أنا أقول أنه من المستحيل إنشاء مرجع غير صالح؟ لا. إنه ممكن تمامًا ، لأن C ++ تسمح لك بعمل أي شيء تقريبًا. من الصعب القيام به دون قصد ، وسوف تكون عن دهشتها من كم عدد الأخطاء غير المتعمدة :)


تعتبر المؤشرات مهمة في العديد من هياكل البيانات التي يتطلب تصميمها القدرة على ربط أو ربط "عقدة" واحدة بآخر بكفاءة. لن "تختار" مؤشر فوق نوع بيانات عادي مثل float ، فهي ببساطة تحتوي على أغراض مختلفة.

تعتبر المؤشرات مفيدة حيث تحتاج إلى أداء عالٍ و / أو بصمة ذاكرة مضغوطة.

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


في C ++ ، إذا كنت ترغب في استخدام polymorphism الفرعي ، لديك إلى مؤشرات المستخدم. انظر هذا المنصب: C ++ تعدد الأشكال دون مؤشرات .

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

إن فكرة وجود متغير يحتفظ بكائن لفئة غير معروفة غير متوافقة مع الوضع الافتراضي (غير المؤشر) لـ C ++ الخاص بتخزين الكائنات على المكدس ، حيث يتوافق مقدار المساحة المخصصة مباشرة مع الفئة. ملاحظة: إذا كان الصف يحتوي على 5 حقول مثيل مقابل 3 ، فسيلزم تخصيص مساحة أكبر.

لاحظ أنه إذا كنت تستخدم '&' لتمرير الوسائط حسب المرجع ، فإن indirection (على سبيل المثال ، المؤشرات) لا يزال متضمنًا وراء الكواليس. إن '&' هو مجرد سكر نحوي (1) يوفر عليك عناء استخدام بنية المؤشر و (2) يسمح للمترجم بأن يكون أكثر صرامة (مثل حظر المؤشرات الفارغة).


في جزء كبير ، المؤشرات هي المصفوفات (في C / C ++) - فهي عناوين في الذاكرة ، ويمكن الوصول إليها مثل صفيف إذا رغبت في ذلك (في الحالات "العادية").

نظرًا لأنهم عنوان أحد العناصر ، فإنهم صغار: فهم لا يأخذون سوى مساحة العنوان. نظرًا لأنها صغيرة ، فإن إرسالها إلى وظيفة رخيصة. ثم تسمح هذه الوظيفة بالعمل على العنصر الفعلي بدلاً من نسخة.

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


في ما يلي نظرة متباينة بعض الشيء ، ولكن تفسر سبب ظهور العديد من ميزات C: http://steve.yegge.googlepages.com/tour-de-babel#C

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

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


لأن نسخ الأشياء الكبيرة في كل الأماكن يضيع الوقت والذاكرة.


ها هو ما أحبه ، ولن أعدك أن أكون خبيراً ، لكني وجدت مؤشرات كبيرة في إحدى المكتبات التي أحاول كتابتها. في هذه المكتبة (إنها واجهة برمجة تطبيقات رسومية مع OpenGL :-)) ، يمكنك إنشاء مثلث يحتوي على كائنات رأسية يتم تمريرها إليها. تأخذ طريقة الرسم هذه الأشياء المثلثية ، وكذلك .. ترسمها بناءً على كائنات قمة الرأس التي قمت بإنشائها. حسنا ، حسنا.

ولكن ، ماذا لو قمت بتغيير إحداثيات قمة الرأس؟ تحريكه أو شيء ما مع moveX () في فئة قمة الرأس؟ حسنا ، حسنا ، الآن لدي لتحديث المثلث ، إضافة المزيد من الأساليب والأداء يجري إضاعة لأن لدي لتحديث المثلث في كل مرة يتحرك قمة الرأس. لا يزال ليس مشكلة كبيرة ، ولكنها ليست كبيرة.

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

مع المؤشرات ، لا تحتاج إلى تحديث المثلثات.

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


يتم وصف الحاجة إلى المؤشرات في لغة C here

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

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

ملاحظة: يرجى الرجوع إلى here للحصول على شرح مفصل مع نموذج التعليمة البرمجية.


  • في بعض الحالات ، مؤشرات الدالة مطلوبة لاستخدام الدالات الموجودة في مكتبة مشتركة (.DLL أو .so). وهذا يشمل تنفيذ العناصر عبر اللغات ، حيث يتم توفير واجهة DLL في كثير من الأحيان.
  • صنع compilers
  • صنع الآلات الحاسبة العلمية ، حيث لديك مصفوفة أو متجه أو خريطة سلسلة من مؤشرات الدالة؟
  • محاولة تعديل ذاكرة الفيديو مباشرة - إنشاء حزمة الرسومات الخاصة بك
  • جعل API!
  • هياكل البيانات - مؤشرات وصلة العقدة للأشجار الخاصة التي تقوم بها

هناك الكثير من الأسباب للمؤشرات. وجود cangling c بشكل خاص مهم في DLLs إذا كنت ترغب في المحافظة على التوافق مع اللغات.





pointers