ال

المؤشرات في C++

1 تمرين

نبذة عن المؤشرات

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

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

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

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

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

الصياغة العامة

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

int* ptr{nullptr}; // Declares a pointer and makes sure it is not invalid

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

std::string opponent{"Solomon Lane"};
// 'ethan' points to the address of the string opponent
std::string* ethan{&opponent};
// Instead of ethan's, the opponent's name address is given to the passPort
std::string passportName{*ethan};

تنبيه: يجب فك الإشارة صراحةً، بينما كانت المراجع تعمل ببساطة كاسم مستعار.

حساب المؤشرات

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

// Stargate Coordinate Code
int gateCode[] = {462, 753, 218, 611, 977};
// 'ptr' points to the first element of 'gateCode'
int* ptr{&gateCode[0]};
// Accesses the third Stargate address through pointer arithmetic
int dialedAddress{*(ptr + 2)};
// Chevron encoded! Dialing Stargate address:
openStarGate(dialedAddress);
Caution

قد يؤدي حساب المؤشرات في C++ بسهولة إلى سلوك غير معرّف إذا لم يُتعامل معه بحذر. قد يظهر السلوك غير المعرّف في صورة نتائج غير متوقعة للبرنامج، أو انهيارات، أو حتى ثغرات أمنية. ومن الأمثلة سيئة الصيت على عواقب السلوك غير المعرّف ما حدث في انفجار صاروخ آريان 5 عام 1996، حيث أدى استثناء برمجي ناتج عن تحويل عدد عشري 64 بت إلى عدد صحيح مُوقَّع 16 بت إلى فشل كارثي.

الوصول إلى متغيرات العضو

في C++، يُستخدم العامل -> للوصول إلى أعضاء كائن ما عبر مؤشر يشير إليه. وهو اختصار يسهّل الوصول إلى أعضاء الكائنات التي تشير إليها المؤشرات. على سبيل المثال، إذا كان ptr مؤشرًا إلى كائن له متغير عضو x، فبدلًا من استخدام (*ptr).x يمكنك استخدام ptr->x مباشرةً. يعزز هذا العامل وضوح الكود ويقلل الإطالة عند التعامل مع مؤشرات إلى الكائنات.

إليك مثالًا موجزًا، باستخدام struct اسمه Superhero وله متغير عضو superpower. تُنشئ الدالة الرئيسية main مؤشرًا dianaPrince إلى كائن Superhero (يمثّل Wonder Woman). ويُستخدم العامل -> للوصول إلى متغير العضو superpower، بما يعرض "Lasso of Truth" الشهيرة لدى Wonder Woman.

struct Superhero {
    std::string superpower;
};

Superhero wonder_woman{};
Superhero* dianaPrince = &wonder_woman;
dianaPrince->superpower = "Lasso of Truth";

// Using the -> operator to access member variable superpower:
std::cout << "Wonder Woman, possesses the mighty " << dianaPrince->superpower;

// Memory cleanup:
delete dianaPrince;

المؤشرات مقابل المراجع

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

Advanced

التخصيص الديناميكي للذاكرة والمؤشرات الفارغة

في C++، يُستخدم new لتخصيص ذاكرة ديناميكيًا للكائنات على الكومة أثناء التنفيذ. وهو يُرجع مؤشرًا إلى عنوان الذاكرة المخصّصة، ما يسمح بأن تكون أعمار الكائنات غير مرتبطة بنطاق معين. ومع ذلك، من المهم أن تقرن new دائمًا بـ delete لتحرير الذاكرة بشكل صحيح. وينبغي استخدام التخصيص الديناميكي للذاكرة بحذر لتجنب مشكلات شائعة مثل تسريبات الذاكرة والوصول إلى ذاكرة محرَّرة.

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

// There is a dark mark bearer, but who is it?
std::string* darkMarkBearer{nullptr};
// It is Bellatrix!
darkMarkBearer = new std::string{"Bellatrix Lestrange"};
// She has been defeated
delete darkMarkBearer;
// Reset pointer to null, symbolizing there is no more darkMarkBearer at the moment
darkMarkBearer = nullptr;

ينبغي أن يحمل المؤشر عنوان كائن صالح، أو أن يُضبط على nullptr. بهذه الطريقة، ما عليك سوى اختبار المؤشرات للتأكد من أنها فارغة، ويمكنك افتراض أن أي مؤشر غير فارغ صالح. وعند تدمير كائن، تبقى أي مؤشرات تشير إلى الكائن المُدمَّر معلّقة (لن تُضبط تلقائيًا على nullptr). وتقع على عاتقك مسؤولية اكتشاف هذه الحالات وضمان ضبط تلك المؤشرات على nullptr بعد ذلك.

في الأكواد الأقدم، قد تصادف بديلين لـ nullptr. أولًا، تُفسَّر القيمة الحرفية 0 تحديدًا على أنها قيمة فارغة للمؤشرات، وإن كانت هذه الحالة الوحيدة التي يمكن فيها إسناد قيمة حرفية صحيحة إلى مؤشر. ثانيًا، preprocessor macro المسمّى NULL، الموروث من C والمعرَّف في الترويسة <cstddef>، تمثيل آخر للمؤشر الفارغ، وإن كان استخدامه أقل شيوعًا في أكواد C++ الحديثة.

تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة

تعلّم المؤشرات

التدريب مقفل

افتح 1 تمرين إضافي لممارسة المؤشرات