مقدمة: لماذا هذه المسألة تربك حتى مبرمجي C++ المحترفين؟
\n\nإذا سبق لك أن فتحت كودًا لشخص آخر بلغة C++ ورأيت شيئًا مثل const int * const data، فبالتأكيد شعرت برغبة في إغلاق المحرر والذهاب لشرب كوب من الشاي. هذا التركيب الغريب مع const المنتشر على جانبي النجمة، هو حجر عثرة حقيقي. سؤال «ما الفرق بين const int* و const int * const و int * const؟» على Stack Overflow حصد مئات الآلاف من المشاهدات. وهذا ليس من قبيل الصدفة.
فهم هذه التراكيب ليس مجرد نزوة من شخص دقيق. بل هو مفتاح لكتابة كود آمن وموثق ذاتيًا، والأهم من ذلك، لاجتياز مقابلات العمل. اليوم سنحسم هذا الارتباك مرة واحدة وإلى الأبد، باستخدام قاعدة بسيطة تعمل دائمًا.
\n\nالأركان الثلاثة: ما الذي سنقارنه؟
\n\nفي لغة C++، يمكن دمج المؤشرات والثبات (constness) بأربع طرق (ثلاثة منها هي الأكثر شيوعًا). دعنا نحددها:
\n\n- \n
- الخيار 1:
const int* ptr(أوint const* ptr— وهما نفس الشيء). \n - الخيار 2:
int * const ptr. \n - الخيار 3:
const int * const ptr(مزيج من الأولين). \n
الفكرة الأساسية: const تشير دائمًا إلى ما هو على يسارها. إذا لم يكن هناك شيء على اليسار، فإنها تشير إلى ما هو على اليمين. يبدو الأمر بسيطًا، لكنه في الممارسة العملية ينقذ الموقف.
\n\nالخيار 1: const int* ptr (مؤشر إلى عدد صحيح ثابت)
\n\nنقرأ من اليمين إلى اليسار: «ptr هو مؤشر إلى عدد صحيح ثابت».
\n\nماذا يعني هذا عمليًا؟
\n- \n
- لا يمكنك تغيير قيمة المتغير الذي يشير إليه ptr من خلال هذا المؤشر. \n
- يمكنك تغيير المؤشر نفسه (جعله يشير إلى متغير آخر). \n
#include <iostream>\n\nint main() {\n int a = 10;\n int b = 20;\n\n const int* ptr = &a; // ptr هو مؤشر إلى const int\n\n // *ptr = 15; // خطأ في الترجمة! لا يمكن تغيير القيمة من خلال const int*\n std::cout << "*ptr = " << *ptr << std::endl; // سيطبع 10\n\n ptr = &b; // صحيح! نغير المؤشر نفسه\n std::cout << "*ptr = " << *ptr << std::endl; // سيطبع 20\n\n return 0;\n}\n\nيُستخدم هذا النوع غالبًا لتمرير البيانات إلى دالة عندما تريد ضمان أن الدالة لن تفسد البيانات الأصلية. على سبيل المثال، void print(const int* data) هو وعد بـ «للقراءة فقط».
الخيار 2: int * const ptr (مؤشر ثابت إلى عدد صحيح)
\n\nنقرأ من اليمين إلى اليسار: «ptr هو مؤشر ثابت إلى عدد صحيح».
\n\nهنا الأمر معكوس:
\n- \n
- يمكنك تغيير قيمة المتغير الذي يشير إليه ptr. \n
- لا يمكنك تغيير المؤشر نفسه (يجب أن يشير دائمًا إلى نفس عنوان الذاكرة). \n
#include <iostream>\n\nint main() {\n int a = 10;\n int b = 20;\n\n int * const ptr = &a; // ptr هو مؤشر ثابت إلى int\n\n *ptr = 15; // صحيح! نغير القيمة من خلال المؤشر\n std::cout << "*ptr = " << *ptr << std::endl; // سيطبع 15\n\n // ptr = &b; // خطأ في الترجمة! لا يمكن تغيير المؤشر نفسه\n\n return 0;\n}\n\nتخيل أن int * const هو مؤشر «مقيد». يُستخدم غالبًا للعمل مع سجلات الأجهزة أو في الحالات التي يجب ألا «يهرب» فيها المؤشر إلى مكان آخر.
الخيار 3: const int * const ptr (مؤشر ثابت إلى عدد صحيح ثابت)
\n\nنقرأ من اليمين إلى اليسار: «ptr هو مؤشر ثابت إلى عدد صحيح ثابت».