ثابت (برمجة حاسوبية)
في برمجة الكمبيوتر ، الثابت هو قيمة لا يغيرها البرنامج أثناء التنفيذ العادي . وعندما يرتبط الثابت بمعرف ، يقال إنه "مُسمى"، على الرغم من أن مصطلحي "ثابت" و"ثابت مُسمى" يُستخدمان غالبًا بالتبادل. ويتناقض هذا مع المتغير ، وهو معرف بقيمة يمكن تغييرها أثناء التنفيذ العادي. لتبسيط الأمر، تظل قيم الثوابت كما هي، بينما تتغير قيم المتغيرات ، ومن هنا جاء اسماهما.
الثوابت مفيدة لكل من المبرمجين والمترجمين : بالنسبة للمبرمجين، فهي شكل من أشكال التعليمات البرمجية التي توثق نفسها وتسمح بالاستدلال على صحتها ، بينما بالنسبة للمترجمين، فهي تسمح بإجراء فحوصات وقت التجميع ووقت التشغيل للتحقق من عدم انتهاك افتراضات الثبات، [أ] وتسمح ببعض تحسينات المترجم أو تبسيطها .
هناك العديد من التجسيدات المحددة للمفهوم العام للثابت، مع تمييزات دقيقة غالبًا ما يتم تجاهلها. وأهمها: الثوابت وقت التجميع (القيمة الثابتة)، والثوابت وقت التشغيل (القيمة الديناميكية)، والكائنات غير القابلة للتغيير ، وأنواع الثوابت ( const).
تتضمن الأمثلة النموذجية لثوابت وقت التجميع الثوابت الرياضية، أو القيم من المعايير (هنا وحدة الإرسال القصوى )، أو قيم التكوين الداخلي (هنا أحرف لكل سطر )، مثل أمثلة C التالية:
const float PI = 3.1415927 ; // أقصى دقة تعويم مفردة const unsigned int MTU = 1500 ; // Ethernet v2, RFC 894 const unsigned int COLUMNS = 80 ;
من الأمثلة النموذجية للثوابت وقت التشغيل القيم المحسوبة استنادًا إلى المدخلات إلى دالة، مثل مثال C++ هذا:
void f ( std :: string s ) { const size_t l = s . length (); // ... }
يستخدم
بعض لغات البرمجة تقوم بالتمييز النحوي الصريح بين الرموز الثابتة والمتغيرة، على سبيل المثال اعتبار التعيين إلى ثابت خطأ نحويًا، بينما في لغات أخرى يعتبران متماثلين نحويًا (كلاهما مجرد معرف)، والفرق في المعالجة دلالي (التعيين إلى معرف صالح نحويًا، ولكن إذا كان المعرف ثابتًا فهو غير صالح دلاليًا).
يتم تعريف قيمة ثابتة مرة واحدة ويمكن الرجوع إليها عدة مرات خلال البرنامج. إن استخدام قيمة ثابتة بدلاً من تحديد نفس القيمة عدة مرات يمكن أن يبسط صيانة الكود (كما في عدم تكرار نفسك ) ويمكن توثيقه ذاتيًا عن طريق توفير اسم ذي معنى لقيمة، على سبيل المثال، PIبدلاً من 3.1415926.
مقارنة مع الحروف والماكرو
توجد عدة طرق رئيسية للتعبير عن قيمة بيانات لا تتغير أثناء تنفيذ البرنامج، وهي متسقة عبر مجموعة كبيرة ومتنوعة من لغات البرمجة. إحدى الطرق الأساسية للغاية هي ببساطة كتابة رقم أو حرف أو سلسلة في كود البرنامج، وهو أمر بسيط في لغات C وC++ واللغات المماثلة.
في لغة التجميع، تتم معالجة الأرقام والأحرف الحرفية باستخدام تعليمات "الوضع الفوري" المتوفرة في معظم المعالجات الدقيقة. يأتي اسم "الوضع الفوري" من توفر القيم فورًا من مجرى التعليمات ، على عكس تحميلها بشكل غير مباشر من خلال البحث عن عنوان ذاكرة. [1] من ناحية أخرى، يتم التعامل مع القيم الأطول من طول كلمة المعالج الدقيق، مثل السلاسل والمصفوفات، بشكل غير مباشر، وتوفر المجمعات عمومًا عملية شبه عملية "بيانات" لتضمين مثل هذه الجداول في البرنامج.
هناك طريقة أخرى تتمثل في تعريف ماكرو رمزي . تقدم العديد من لغات البرمجة عالية المستوى، والعديد من برامج التجميع، مرفق ماكرو حيث يمكن للمبرمج تعريف أسماء لقيم مختلفة، عادةً في بداية ملف المصدر أو في ملف تعريف منفصل. ثم يستبدل المعالج المسبق هذه الأسماء بالقيم المناسبة قبل التجميع، مما ينتج عنه شيء متطابق وظيفيًا مع استخدام القيم الحرفية، مع مزايا السرعة في الوضع الفوري. نظرًا لأنه قد يكون من الصعب صيانة الكود حيث تتم كتابة جميع القيم حرفيًا، فإذا تم استخدام قيمة بأي طريقة متكررة أو غير واضحة، فغالبًا ما يتم تسميتها بماكرو.
الطريقة الثالثة هي عن طريق إعلان وتعريف متغير على أنه "ثابت". يمكن إعلان متغير عالمي أو متغير ثابتconst (أو رمز محدد في التجميع) باستخدام مؤهل كلمة رئيسية مثل , constant, أو final, مما يعني أن قيمته سيتم تعيينها في وقت التجميع ولا ينبغي تغييرها في وقت التشغيل. يضع المترجمون عمومًا ثوابت ثابتة في قسم النص من ملف الكائن مع الكود نفسه، على عكس قسم البيانات حيث يتم الاحتفاظ بالبيانات الأولية غير الثابتة. يمكن لبعض المترجمين إنتاج قسم مخصص خصيصًا للثوابت. يمكن تطبيق حماية الذاكرة على هذه المنطقة لمنع الكتابة فوق مثل هذه الثوابت بواسطة مؤشرات خاطئة.
تختلف هذه الثوابت عن الثوابت الحرفية في عدد من الطرق. يضع المترجمون عادةً ثابتًا في موقع ذاكرة واحد يتم تحديده بواسطة رمز، بدلاً من نشره في جميع أنحاء الملف القابل للتنفيذ كما هو الحال مع الماكرو. وبينما يستبعد هذا مزايا السرعة في الوضع الفوري، فهناك مزايا في كفاءة الذاكرة، ويمكن للمصححين العمل مع هذه الثوابت في وقت التشغيل. أيضًا، بينما قد يتم إعادة تعريف الماكرو عن طريق الخطأ بواسطة ملفات رأس متضاربة في C وC++، يتم اكتشاف الثوابت المتضاربة في وقت التجميع.
اعتمادًا على اللغة، يمكن أن تكون الثوابت غير مكتوبة أو مكتوبة. في C وC++، توفر وحدات الماكرو الأمر الأول، بينما constتوفر الأمر الثاني:
#تعريف PI 3.1415926535
ثابت تعويم pi2 = 3.1415926535 ؛
بينما في Ada، هناك أنواع رقمية عالمية يمكن استخدامها، إذا رغبت في ذلك:
باي : ثابت := 3.1415926535 ؛
pi2 : ثابت عائم := 3.1415926535 ؛
مع تحويل المتغير غير المكتوب ضمناً إلى النوع المناسب عند كل استخدام. [2]
الثوابت ذات القيمة الديناميكية
بالإضافة إلى الثوابت الثابتة الموضحة أعلاه، فإن العديد من اللغات الإجرائية مثل Ada وC++ تعمل على توسيع مفهوم الثبات نحو المتغيرات العالمية التي يتم إنشاؤها في وقت التهيئة، والمتغيرات المحلية التي يتم إنشاؤها تلقائيًا في وقت التشغيل على المكدس أو في السجلات، إلى الذاكرة المخصصة ديناميكيًا والتي يتم الوصول إليها من خلال المؤشر، وقوائم المعلمات في رؤوس الوظائف.
لا تشير الثوابت ذات القيمة الديناميكية إلى متغير على أنه موجود في منطقة معينة من الذاكرة، ولا يتم تعيين القيم في وقت التجميع. في كود C++ مثل
دالة float ( ثابتة float أي شيء ) { ثابتة float XYZ = someGlobalVariable * someOtherFunction ( أي شيء )؛ ... }
التعبيرات التي تم تهيئة الثابت لها ليست ثابتة بحد ذاتها. لا يعد استخدام الثبات ضروريًا هنا لضمان شرعية البرنامج أو صحته الدلالية، ولكن له ثلاث مزايا:
- من الواضح للقارئ أن الكائن لن يتم تعديله أكثر، بمجرد ضبطه
- سيتم رفض محاولات تغيير قيمة الكائن (من قبل المبرمجين اللاحقين الذين لا يفهمون منطق البرنامج بشكل كامل) بواسطة المترجم
- قد يكون المترجم قادرًا على إجراء تحسينات على الكود مع العلم أن قيمة الكائن لن تتغير بمجرد إنشائه. [3]
نشأت الثوابت ذات القيمة الديناميكية كميزة لغوية مع ALGOL 68. [ 3] أظهرت دراسات أكواد Ada وC++ أن الثوابت ذات القيمة الديناميكية تُستخدم بشكل غير متكرر، عادةً لـ 1% أو أقل من الكائنات، بينما يمكن استخدامها أكثر من ذلك بكثير، حيث أن حوالي 40-50% من الكائنات المحلية غير الفئوية تكون ثابتة بالفعل بمجرد إنشائها. [3] [4] من ناحية أخرى، تميل مثل هذه "المتغيرات غير القابلة للتغيير" إلى أن تكون الافتراضية في اللغات الوظيفية لأنها تفضل أنماط البرمجة بدون آثار جانبية (على سبيل المثال، التكرار) أو تجعل معظم الإعلانات غير قابلة للتغيير افتراضيًا، مثل ML . حتى أن اللغات الوظيفية البحتة تحظر الآثار الجانبية تمامًا.
غالبًا ما يتم استخدام الثبات في إعلانات الوظائف، كوعد بأنه عند تمرير كائن عن طريق المرجع، لن تغيره الوظيفة التي يتم استدعاؤها. اعتمادًا على بناء الجملة، قد يكون المؤشر أو الكائن الذي يتم الإشارة إليه ثابتًا، ولكن عادةً ما يكون الأخير مرغوبًا. وخاصة في C++ وC، تسمى عملية ضمان ثبات هياكل البيانات المناسبة طوال البرنامج بالتصحيح الثابت .
معاملات الدالة الثابتة
في لغة C/C++، من الممكن إعلان معلمة دالة أو طريقة على أنها ثابتة. وهذا يضمن عدم إمكانية تعديل هذه المعلمة عن غير قصد بعد تهيئتها بواسطة المتصل. إذا كانت المعلمة من نوع محدد مسبقًا (مضمّن)، يتم استدعاؤها بالقيمة ولا يمكن تعديلها. إذا كانت من نوع محدد من قبل المستخدم، يكون المتغير هو عنوان المؤشر، والذي لا يمكن تعديله أيضًا. ومع ذلك، يمكن تعديل محتوى الكائن دون حدود. قد يكون إعلان المعلمات على أنها ثوابت طريقة للإشارة إلى أنه لا ينبغي تغيير هذه القيمة، ولكن يجب على المبرمج أن يضع في اعتباره أن عمليات التحقق من تعديل الكائن لا يمكن أن يقوم بها المترجم.
بالإضافة إلى هذه الميزة، من الممكن أيضًا في C++ إعلان وظيفة أو طريقة باسم const. وهذا يمنع مثل هذه الوظائف أو الطرق من تعديل أي شيء سوى المتغيرات المحلية.
في لغة C#، توجد الكلمة الأساسية const، لكنها لا تتمتع بنفس التأثير على معلمات الوظيفة، كما هو الحال في لغة C/C++. ومع ذلك، توجد طريقة "لتحريك" المترجم لإجراء الفحص، وإن كانت صعبة بعض الشيء. [5]
الثوابت الموجهة للكائنات
يُشار إلى بنية البيانات الثابتة أو الكائن باسم " غير قابل للتغيير " في لغة البرمجة الموجهة للكائنات. يمنح عدم قابلية الكائن للتغيير بعض المزايا في تصميم البرنامج. على سبيل المثال، يمكن "نسخه" ببساطة عن طريق نسخ مؤشره أو مرجعه، مما يتجنب عملية النسخ التي تستغرق وقتًا طويلاً ويحافظ على الذاكرة.
تمتد اللغات الموجهة للكائنات مثل C++ إلى ما هو أبعد من ذلك. يمكن جعل الأعضاء الفردية للهيكل أو الفئة ثابتة حتى لو لم تكن الفئة ثابتة. وعلى العكس من ذلك، mutableتسمح الكلمة الأساسية بتغيير عضو الفئة حتى لو تم إنشاء كائن كـ const.
حتى الوظائف يمكن أن تكون ثابتة في C++. والمعنى هنا هو أنه لا يمكن استدعاء سوى وظيفة ثابتة لكائن تم إنشاؤه كـ const؛ ولا تغير الوظيفة الثابتة أي بيانات غير قابلة للتغيير.
تحتوي لغة C# على كل من المؤهل a constوالمؤهل a readonly؛ حيث أن الثابت الخاص به مخصص فقط لثوابت وقت التجميع، بينما readonlyيمكن استخدامه في المنشئين وتطبيقات وقت التشغيل الأخرى.
جافا
تحتوي لغة Java على مؤهل يسمى . finalيمنع تغيير المرجع ويضمن عدم الإشارة إلى كائن مختلف. هذا لا يمنع إجراء تغييرات على الكائن المشار إليه نفسه. finalإن مؤهلات Java تعادل بشكل أساسي const المؤشر في لغة C++. وهي لا توفر الميزات الأخرى لـ const.
في Java ، يشير المؤهل finalإلى أن عضو البيانات أو المتغير المتأثر غير قابل للتعيين، كما هو موضح أدناه:
final int i = 3 ; i = 4 ; // خطأ! لا يمكن تعديل كائن "نهائي"
يجب أن يكون من الممكن تحديد ذلك بواسطة المترجمين حيث يتم تهيئة المتغير الذي finalيحمل العلامة، ويجب تنفيذ ذلك مرة واحدة فقط، وإلا فلن يتم تجميع الفصل. الكلمات الأساسية في Java finalوC++ constلها نفس المعنى عند تطبيقها مع المتغيرات البدائية.
const int i = 3 ; // إعلان C++ i = 4 ; // خطأ!
عند النظر إلى المؤشرات، finalفإن المرجع في Java يعني شيئًا مشابهًا constللمؤشر في C++. في C++، يمكن للمرء أن يعلن عن "نوع مؤشر ثابت".
Foo * const bar = mem_location ; // نوع المؤشر الثابت
هنا، barيجب أن يتم تهيئة المرجع في وقت الإعلان ولا يمكن تغييره مرة أخرى، ولكن ما يشير إليه قابل للتعديل. أي أنه صالح. ولكن لا يمكنه الإشارة إلى موقع آخر. تعمل المراجع النهائية في Java بنفس الطريقة باستثناء أنه يمكن إعلانها دون تهيئة.
*bar = value
النهائي Foo i ; // إعلان Java
ملاحظة: لا تدعم Java المؤشرات. [6] وذلك لأن المؤشرات (مع القيود) هي الطريقة الافتراضية للوصول إلى الكائنات في Java، ولا تستخدم Java النجوم للإشارة إليها. على سبيل المثال، i في المثال الأخير هو مؤشر ويمكن استخدامه للوصول إلى المثيل.
يمكنك أيضًا إعلان مؤشر لبيانات "القراءة فقط" في C++.
ثابت Foo * بار ؛
يمكن تعديل هذه barالقيمة للإشارة إلى أي شيء، في أي وقت؛ ولكن لا يمكن تعديل هذه القيمة المشار إليها من خلال bar المؤشر.
لا توجد آلية مكافئة في Java. وبالتالي لا توجد constطرق أيضًا. لا يمكن فرض صحة الثوابت في Java، على الرغم من أنه من خلال استخدام الواجهات وتحديد واجهة للقراءة فقط للفئة وتمريرها، يمكن للمرء ضمان إمكانية تمرير الكائنات حول النظام بطريقة لا يمكن تعديلها.
يوفر إطار عمل مجموعات Java طريقة لإنشاء غلاف ثابت لـ Collectionvia والطرق المماثلة.
Collections.unmodifiableCollection()
من الممكن إعلان الطريقة في Java على أنها "نهائية"، مما يعني أنه لا يمكن تجاوزها في الفئات الفرعية.
سي شارب
في C# ، يكون للمؤهل readonlyنفس التأثير على عناصر البيانات كما finalهو الحال في Java و constdoes في C++؛ ويكون للمعدِّل constتأثير مماثل (ولكنه مكتوب ومحدد على مستوى الفئة) لتأثير في #defineC++. أما التأثير الآخر الذي يمنع الوراثة في Java finalعند تطبيقه على الأساليب والفئات في C# فيتم إحداثه بمساعدة الكلمة الأساسية sealed.
على عكس لغة C++، لا تسمح لغة C# بوضع علامة على الأساليب والمعلمات على أنها const. ومع ذلك، يمكن للمرء أيضًا تمرير فئات فرعية للقراءة فقط، ويوفر إطار عمل .NET بعض الدعم لتحويل المجموعات القابلة للتغيير إلى مجموعات غير قابلة للتغيير والتي يمكن تمريرها كأغلفة للقراءة فقط.
حسب النموذج
تختلف معالجة الثوابت بشكل كبير حسب نموذج البرمجة . تعد صحة الثوابت مشكلة في اللغات الآمرة مثل C++ لأن ارتباطات الأسماء تنشئ بشكل افتراضي متغيرات ، والتي يمكن أن تختلف، كما يوحي الاسم، وبالتالي إذا رغب المرء في وضع علامة على ارتباط ثابت، فهذا يتطلب بعض الدلالة الإضافية. [ب] في نماذج لغات البرمجة الأخرى، تنشأ مشكلات ذات صلة، مع وجود بعض النظائر لصحّة الثوابت.
في البرمجة الوظيفية ، تكون البيانات ثابتة بشكل افتراضي، وليس متغيرة بشكل افتراضي. بدلاً من تعيين قيمة لمتغير (مساحة تخزين باسم وقيمة متغيرة محتملة)، يقوم المرء بإنشاء ربط لاسم بقيمة، مثل البناء letفي العديد من لهجات Lisp . في بعض اللغات الوظيفية، وخاصة تلك متعددة النماذج مثل Common Lisp ، يعد تعديل البيانات أمرًا شائعًا، بينما في لغات أخرى يتم تجنبه أو اعتباره استثنائيًا؛ هذه هي الحال بالنسبة لـ Scheme (لهجة Lisp أخرى)، والتي تستخدم set!البناء لتعديل البيانات، مع لفت الانتباه إلى هذا من خلال علامة التعجب ! . تحقق مثل هذه اللغات أهداف صحة الثبات افتراضيًا، وتلفت الانتباه إلى التعديل بدلاً من الثبات.
في عدد من لغات البرمجة الموجهة للكائنات ، يوجد مفهوم الكائن غير القابل للتغيير ، والذي يستخدم بشكل خاص للأنواع الأساسية مثل السلاسل؛ ومن الأمثلة البارزة على ذلك Java وJavaScript وPython وC#. تختلف هذه اللغات في ما إذا كان من الممكن وضع علامة على الأنواع المحددة من قبل المستخدم على أنها غير قابلة للتغيير، وقد تسمح بوضع علامة على حقول (سمات) معينة لكائن أو نوع على أنها غير قابلة للتغيير.
في بعض لغات البرمجة متعددة النماذج التي تسمح بالأنماط الموجهة للكائنات والأنماط الوظيفية، قد يتم الجمع بين هاتين الميزتين. على سبيل المثال، في OCaml تكون حقول الكائنات غير قابلة للتغيير بشكل افتراضي ويجب تمييزها صراحةً بالكلمة الأساسية mutableلتكون قابلة للتغيير، بينما في Scala، تكون الارتباطات غير قابلة للتغيير بشكل صريح عند تعريفها باستخدام val"value" وقابلة للتغيير بشكل صريح عند تعريفها باستخدام var"variable".
اتفاقيات التسمية
تختلف اتفاقيات تسمية الثوابت. فبعضها يسميها ببساطة كما يسمي أي متغير آخر. ويستخدم البعض الآخر الأحرف الكبيرة والشرطات السفلية للثوابت بطريقة مماثلة لاستخدامها التقليدي للماكرو الرمزي، مثل SOME_CONSTANT. [7] في التدوين المجري ، تشير البادئة "k" إلى الثوابت وكذلك الماكرو والأنواع المعدودة .
أحد الاتفاقيات المفروضة في Ruby هي أن أي متغير يبدأ بحرف كبير يُعتبر ثابتًا، بما في ذلك أسماء الفئات.
انظر أيضا
- ثوابت العناوين لمنصة IBM /360 و Z/Architecture
ملحوظات
- ^ في بعض الحالات، يمكن التحايل على ذلك، على سبيل المثال باستخدام الكود الذي يعدل نفسه أو عن طريق الكتابة فوق موقع الذاكرة حيث يتم تخزين القيمة.
- ^ هذا ليس عالميًا: في Ada تكون معلمات الإدخال ومعلمات الحلقة ثابتة ضمنيًا، على سبيل المثال.
مراجع
- ^ مثال: معلومات أنظمة IBM. مجموعة التعليمات - مرجع لغة التجميع لبرنامج PowerPC.
- ^ Booch, Grady (1983). هندسة البرمجيات مع آدا . بنيامين كومينجز . ص 116-117. ISBN 0-8053-0600-5.
- ^ abc Schilling, Jonathan L. (أبريل 1995). "الثوابت ذات القيمة الديناميكية: ميزة لغة غير مستخدمة بشكل كافٍ". إشعارات SIGPLAN . 30 (4): 13–20. doi : 10.1145/202176.202177 . S2CID 17489672.
- ^ بيركنز، جيه إيه ممارسات البرمجة: تحليل مصدر آدا المطوَّر للقوات الجوية والجيش والبحرية . وقائع مؤتمر تري-آدا 89. ص 342-354. doi :10.1145/74261.74287.
- ^ Timwi (2010-09-09). "معلمات دالة للقراءة فقط ("مثل const") في C#". Stack Overflow . تم الاسترجاع في 2012-05-06 .
[...] يمكنك بعد ذلك إعلان الأساليب التي "يخبرك" نوع معلماتها بما إذا كانت تخطط لتغيير المتغير أم لا: [...] يحاكي هذا فحوصات وقت التجميع المشابهة للثبات في C++. وكما أشار إريك ليبرت بشكل صحيح، فإن هذا ليس هو نفسه الثبات. ولكن بصفتي مبرمج C++، أعتقد أنك تعرف ذلك.
- ^ "شبكة تقنية أوراكل لمطوري جافا | شبكة تقنية أوراكل | أوراكل". Java.sun.com. 2013-08-14 . تم الاسترجاع في 2013-08-18 .
- ^ مطور Microsoft Office XP: أسماء ثابتة
