ثابت (برمجة الحاسوب)
في بعض لغات البرمجة ، يُعدّ `const` مُحدِّدًا للنوع ( كلمة مفتاحية تُطبَّق على نوع البيانات ) يُشير إلى أن البيانات للقراءة فقط. مع أنّه يُمكن استخدامه لتعريف الثوابت ، إلا أنّه constفي عائلة لغات C يختلف عن البنى المُشابهة في اللغات الأخرى، إذ يُعدّ جزءًا من النوع ، وبالتالي يُؤدّي إلى سلوك مُعقّد عند دمجه مع المؤشرات ، والمراجع، وأنواع البيانات المُركَّبة ، والتحقّق من النوع . في لغات أخرى، لا تُخزَّن البيانات في موقع ذاكرة واحد ، بل تُنسخ في وقت الترجمة لكل استخدام. [ 1 ] تشمل اللغات التي تستخدمه: C و C++ و D و JavaScript و Julia و Rust .
مقدمة
عند استخدامها في تعريف كائن ، تشير [ a ] إلى أن الكائن ثابت : لا يمكن تغيير قيمته ، على عكس المتغير . هذا الاستخدام الأساسي - تعريف الثوابت - له نظائر في العديد من اللغات الأخرى.
مع ذلك، على عكس اللغات الأخرى، في عائلة لغات C، constيُعدّ `x` جزءًا من النوع ، وليس جزءًا من الكائن . على سبيل المثال، في لغة C، يُصرّح عن كائن من النوع `x`، حيث يُعدّ `x` جزءًا من النوع، كما لو تم تحليله على النحو التالي: `(const int) x`. بينما في لغة Ada ، يُصرّح عن ثابت (نوع من الكائنات) من النوع `x`: حيث يُعدّ `x` جزءًا من الكائن ، ولكنه ليس جزءًا من النوع .constintx=1;xconst intconstX:constantINTEGER:=1_XINTEGERconstant
يترتب على ذلك نتيجتان دقيقتان. أولاً، constيمكن تطبيق ذلك على أجزاء من نوع أكثر تعقيدًا - على سبيل المثال، const int* const x;يُعلن عن مؤشر ثابت إلى عدد صحيح ثابت، بينما const int* x;يُعلن عن مؤشر متغير إلى عدد صحيح ثابت، int* const x;ويُعلن عن مؤشر ثابت إلى عدد صحيح متغير. ثانيًا، نظرًا لأن constجزءًا من النوع، يجب أن يتطابق كجزء من فحص النوع. على سبيل المثال، الكود التالي غير صالح:
void f ( int & x ); // ... const int i ; f ( i );لأن الوسيط المُمرَّر إلى الدالة fيجب أن يكون عددًا صحيحًا متغيرًا ، بينما الوسيط المُمرَّر إلى iالدالة عدد صحيح ثابت . يُعد هذا التطابق شكلًا من أشكال صحة البرنامج ، ويُعرف باسم صحة الثوابت . يسمح هذا بنوع من البرمجة التعاقدية ، حيث تُحدد الدوال، كجزء من توقيع نوعها، ما إذا كانت تُعدِّل وسائطها أم لا، وما إذا كانت قيمتها المُعادة قابلة للتعديل أم لا. يُعد فحص النوع هذا ذا أهمية بالغة في المؤشرات والمراجع - وليس في أنواع القيم الأساسية مثل الأعداد الصحيحة - ولكنه مهم أيضًا لأنواع البيانات المركبة أو الأنواع المُنمذجة مثل الحاويات . يُخفى هذا الفحص نظرًا لإمكانية constحذف الوسيط المُمرَّر في كثير من الأحيان، بسبب تحويل النوع الضمني ( تحويل النوع الضمني ) ولأن لغة C تعتمد على استدعاء القيمة (بينما تعتمد لغتا C++ وD إما على استدعاء القيمة أو استدعاء المرجع).
عواقب
لا تعني فكرة الثبات أن المتغير كما هو مخزن في ذاكرة الحاسوب غير قابل للكتابة. بل إن constالثبات هو بنية برمجية تُستخدم أثناء الترجمة لتحديد ما ينبغي على المبرمج فعله، وليس بالضرورة ما يمكنه فعله. مع ذلك، تجدر الإشارة إلى أنه في حالة البيانات المُعرَّفة مسبقًا (مثل const char*السلاسل النصية )، غالبًا ماconst تكون لغة C غير قابلة للكتابة.
التمييز عن الثوابت
بينما لا تتغير قيمة الثابت أثناء تشغيل البرنامج، constقد تتغير قيمة الكائن المُعلن عنه أثناء تشغيل البرنامج. ومن الأمثلة الشائعة على ذلك سجلات القراءة فقط في الأنظمة المدمجة، مثل الحالة الحالية للمدخل الرقمي. غالبًا ما تُعلن سجلات بيانات المدخلات الرقمية باسم `<data>` constو`< data> volatile`. قد يتغير محتوى هذه السجلات دون تدخل البرنامج (`<data> volatile`)، ولكن من غير الصحيح أن يحاول البرنامج الكتابة إليها (`<data> const`).
استخدامات أخرى
بالإضافة إلى ذلك، يمكن تعريف دالة عضو (غير ثابتة) على النحو التالي: `const` const. في هذه الحالة، يكون thisالمؤشر داخل هذه الدالة من النوع `const` const T*وليس من النوع `const` فقط T*. [ 2 ] هذا يعني أنه لا يمكن استدعاء الدوال غير الثابتة لهذا الكائن من داخل هذه الدالة، كما لا يمكن تعديل متغيرات الأعضاءmutable . في لغة C++، يمكن تعريف متغير عضو على النحو التالي : `const`، مما يشير إلى أن هذا القيد لا ينطبق عليه. في بعض الحالات، قد يكون هذا مفيدًا، على سبيل المثال مع التخزين المؤقت ، وحساب المراجع ، ومزامنة البيانات . في هذه الحالات، يظل المعنى المنطقي (الحالة) للكائن دون تغيير، لكن الكائن ليس ثابتًا فعليًا لأن تمثيله الثنائي قد يتغير.
بناء الجملة
في لغات C وC++ وD، يُمكن تعريف جميع أنواع البيانات، بما في ذلك تلك التي يُحددها المستخدم، constعلى أنها ثابتة (const-correctness)، ويُلزم مبدأ الثبات (const-correctness) بتعريف جميع المتغيرات أو الكائنات على هذا النحو ما لم تكن هناك حاجة لتعديلها. constيُسهّل هذا الاستخدام الاستباقي للثوابت فهم القيم وتتبعها وتحليلها [ 3 ] ، مما يزيد من قابلية قراءة الكود وفهمه، ويُبسّط العمل الجماعي وصيانة الكود لأنه يُوضّح الاستخدام المقصود للقيمة. يُساعد هذا المُصرّف والمطور على حدٍ سواء في تحليل الكود، كما يُتيح للمُصرّف المُحسّن توليد كود أكثر كفاءة [ 4 ] .
أنواع البيانات البسيطة
بالنسبة لأنواع البيانات البسيطة غير المؤشرية، constيُعدّ تطبيق المُحدِّد أمرًا مباشرًا. يمكن وضعه على أيٍّ من جانبي بعض الأنواع لأسباب تاريخية (على سبيل المثال، const char foo = 'a';يُعادل char const foo = 'a';). في بعض التطبيقات، يُؤدّي استخدامه constمرتين (على سبيل المثال، const char constأو char const const) إلى ظهور تحذير وليس خطأ.
إرشادات ومراجع
بالنسبة لأنواع المؤشرات والمراجع، يكون المعنى constأكثر تعقيدًا - فقد يكون المؤشر نفسه، أو القيمة التي يُشير إليها، أو كلاهما، مؤشرًا const. علاوة على ذلك، قد يكون بناء الجملة مُربكًا. يمكن تعريف المؤشر كمؤشر constإلى قيمة قابلة للكتابة، أو كمؤشر قابل للكتابة إلى constقيمة، أو constكمؤشر إلى constقيمة.لا يمكن إعادة تعيين مؤشر constليشير إلى كائن مختلف عن الكائن الذي تم تعيينه له في البداية، ولكن يمكن استخدامه لتعديل القيمة التي يشير إليها (وتسمى الكائن المُشار إليه ). [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] تُعد متغيرات المراجع في لغة C++ صيغة بديلة constللمؤشرات. من ناحية أخرى، يمكن إعادة تعيين مؤشر إلى constكائن ليشير إلى موقع ذاكرة آخر (يجب أن يكون كائنًا من نفس النوع أو من نوع قابل للتحويل)، ولكن لا يمكن استخدامه لتعديل الذاكرة التي يشير إليها. يمكن أيضًا تعريف constمؤشر إلى constكائن، ولا يمكن استخدامه لتعديل الكائن المُشار إليه أو إعادة تعيينه ليشير إلى كائن آخر. يوضح الكود التالي هذه الفروق الدقيقة:
void foo ( int * p , const int * cp , int * const pc , const int * const cpc ) { * p = 0 ; // صحيح: يُعدّل البيانات المشار إليها p = NULL ; // صحيح: يُعدّل المؤشر* cp = 0 ; // خطأ! لا يمكن تعديل البيانات المشار إليها cp = NULL ; // صحيح: تم تعديل المؤشر* pc = 0 ; // صحيح: يتم تعديل البيانات المشار إليها pc = NULL ; // خطأ! لا يمكن تعديل المؤشر* cpc = 0 ; // خطأ! لا يمكن تعديل البيانات المشار إليها cpc = NULL ; // خطأ! لا يمكن تعديل المؤشر }اتفاقية C
وفقًا لاتفاقية لغة C المعتادة في التصريحات، يأتي التصريح بعد الاستخدام، *ويُكتب `in` في المؤشر على المؤشر نفسه، مما يشير إلى فك المرجع . على سبيل المثال، في التصريح `in` int *p، يكون الشكل المفكوك مرجعيًا *pهو `in` int، بينما يكون الشكل المرجعي pمؤشرًا إلى `in` int. وبالتالي، constيُعدِّل `in` الاسم الموجود على يمينه. أما اتفاقية لغة C++، فهي ربط `in` *بالنوع، كما في `in` int* p، وقراءة `in` constعلى أنه يُعدِّل النوع الموجود على يساره. const int * cpوبالتالي، يمكن قراءة `in` على أنه ` *cpin` const int(القيمة ثابتة)، أو ` cpin` const int *(المؤشر هو مؤشر إلى عدد صحيح ثابت).
int * p ; // *p قيمة عددية صحيحة const int * cp ; // *cp عدد صحيح ثابت int * const pc ; // pc عدد صحيح ثابت int* const int * const cpc ; // cpc مؤشر ثابت ويشير إلى قيمة ثابتةاتفاقية لغة C++
باتباع اصطلاح لغة C++ في تحليل النوع لا القيمة، يُنصح بقراءة تعريف النوع من اليمين إلى اليسار. بالتالي، يُمكن اعتبار كل ما يقع على يسار النجمة مؤشرًا للنوع المُشار إليه، بينما يُعتبر كل ما يقع على يمينها مؤشرًا للقيمة. على سبيل المثال، في المثال السابق، const int*يُمكن قراءة `<input>` كمؤشر قابل للكتابة يُشير إلى عدد صحيح غير قابل للكتابة، بينما int* constيُمكن قراءة `<input>` كمؤشر غير قابل للكتابة يُشير إلى عدد صحيح قابل للكتابة.
هناك قاعدة عامة أكثر تساعد في فهم التصريحات والتعريفات المعقدة، وهي كالتالي:
- ابحث عن مُعرِّف الإعلان المعني
- اقرأ إلى أقصى اليمين قدر الإمكان (أي حتى نهاية الإعلان أو إلى القوس الختامي التالي، أيهما يأتي أولاً).
- ارجع إلى حيث بدأت القراءة، واقرأ للخلف إلى اليسار (أي حتى بداية الإعلان أو إلى القوس المفتوح الذي يطابق القوس المغلق الموجود في الخطوة السابقة).
- بمجرد الوصول إلى بداية الإعلان، أنهِ العملية. وإذا لم يكن الأمر كذلك، فتابع من الخطوة 2، بعد القوس الختامي الذي تمت مطابقته في المرة الأخيرة.
إليك مثال:
| جزء من التعبير | double ( ** const ( * fun ( int ))( double ))[ 10 ] | المعنى (قراءة من أعلى إلى أسفل) |
|---|---|---|
| المعرف | هزار | funهو ... |
| اقرأ إلى اليمين | ( عدد صحيح )) | دالة تتوقع int... |
| ابحث عن التطابق ( | ( * | إرجاع مؤشر إلى ... |
| تابع إلى اليمين | ( مزدوج )) | دالة تتوقع double... |
| ابحث عن التطابق ( | ( ** ثابت | إرجاع مؤشر ثابت إلى مؤشر إلى ... |
| تابع إلى اليمين | [ 10 ] | مجموعات من 10 ... |
| اقرأ إلى اليسار | مزدوج | doubleس. |
عند القراءة من اليسار، من المهم قراءة العناصر من اليمين إلى اليسار. لذا const int*يصبح `an` مؤشرًا إلى `a`const int وليس constمؤشرًا إلى `an`int .
في بعض الحالات، تسمح لغة C/C++ constبوضع الكلمة المفتاحية على يمين النوع. إليك بعض الأمثلة:
const int * cp ; // مكافئ لـ int const* cp, const int * const cpc ; // مكافئ لـ int const* const cpcعلى الرغم من أن لغة C/C++ تسمح بمثل هذه التعريفات (التي تُطابق اللغة الإنجليزية تقريبًا عند قراءة التعريفات من اليسار إلى اليمين)، إلا أن المُصرّف لا يزال يقرأ التعريفات وفقًا للإجراء المذكور أعلاه: من اليمين إلى اليسار. لكن وضع constقبل ما يجب أن يكون ثابتًا يُؤدي سريعًا إلى عدم تطابق بين ما يُراد كتابته وما يقرره المُصرّف أنه كُتب. لنأخذ على سبيل المثال المؤشرات إلى المؤشرات:
int ** pp ; // مؤشر إلى مؤشر إلى أعداد صحيحة const int ** cpp ; // مؤشر إلى مؤشر إلى قيمة عددية صحيحة ثابتة (ليس مؤشرًا إلى مؤشر ثابت إلى أعداد صحيحة) int * const * pcp ; // مؤشر إلى مؤشر ثابت إلى قيم عددية صحيحة (ليس مؤشرًا ثابتًا إلى مؤشر إلى أعداد صحيحة) int ** const ppc ; // مؤشر ثابت إلى مؤشرات إلى أعداد صحيحة (كون ppc، المعرّف، ثابتًا لا معنى له) const int ** const cppc ; // مؤشر ثابت إلى مؤشرات إلى قيم عددية صحيحة ثابتةيختار البعض كتابة رمز المؤشر على المتغير، معتقدين أن إرفاقه بالنوع قد يكون مربكًا، لأنه يوحي بقوة بنوع المؤشر وهو ليس بالضرورة هو الحال في لغة C.
// طريقتان: int * a ; // علامة النجمة محاذية لليسار int * a ; // علامة النجمة محاذية لليمينint * a , b ; // مُربك (a مؤشر إلى عدد صحيح بينما b عدد صحيح فقط) int * a , b ; // a مؤشر إلى عدد صحيح و b عدد صحيحint * a , * b ; // طريقة كتابة غير أنيقة (كلاهما مؤشران إلى أعداد صحيحة، لكن هذه طريقة غير مريحة للكتابة) int * a , * b ;لاحظ أنه في لغة C# ، فإن محاذاة علامة النجمة إلى اليمين غير مسموح بها. [ 10 ]
int * a , * b ; // غير مسموح به في لغة C# int * a , b ; // يُعلن عن متغيرين من نوع int*توصي الأسئلة الشائعة لبيارن ستروستروب بتعريف متغير واحد فقط في كل سطر عند استخدام اصطلاح لغة C++، لتجنب هذه المشكلة. [ 11 ]
تنطبق الاعتبارات نفسها على تعريف المراجع ومراجع القيمة اليمنى:
int i = 22 ; const int & cr = i ; const int & cr2 = i , cr3 = i ; // مُربك: // cr2 مرجع، لكن cr3 ليس كذلك: // cr3 عدد صحيح ثابت مُهيأ بقيمة i// خطأ: لا يمكن تغيير المراجع على أي حال. int & const rc = myInt ;// C++: int && rref = int ( 5 ), value = 10 ; // مُربك: rref هو مرجع قيمة يمين، لكن value هو عدد صحيح فقط. int && rref = int ( 5 ), value = 10 ;تزداد تعقيدًا التصريحات عند استخدام المصفوفات متعددة الأبعاد والمراجع (أو المؤشرات) إلى المؤشرات. ورغم أن البعض يرى أن هذه التصريحات مُربكة وعرضة للأخطاء، وأنه ينبغي تجنبها أو استبدالها بهياكل ذات مستوى أعلى، إلا أن الإجراء الموضح في بداية هذا القسم يُمكن استخدامه دائمًا دون إحداث أي لبس أو غموض.
المعلمات والمتغيرات
constيمكن تعريف المتغيرات في كل من معاملات الدوال والمتغيرات ( الثابتة أو التلقائية، بما في ذلك المتغيرات العامة والمحلية). ويختلف تفسيرها باختلاف الاستخدامات. constالمتغير الثابت ( المتغير العام أو المتغير المحلي الثابت ) هو قيمة ثابتة، ويمكن استخدامه لبيانات مثل الثوابت الرياضية، const double PI = 3.14159أو معاملات وقت الترجمة. أما constالمتغير التلقائي (المتغير المحلي غير الثابت) فيعني أنه يتم تعيين قيمة واحدة فقط ، مع إمكانية استخدام قيمة مختلفة في كل مرة const int x_squared = x * x. constفي حالة تمرير المعامل بالمرجع، لا يتم تعديل القيمة المشار إليها، فهي جزء من العقد . بينما constفي حالة تمرير المعامل بالقيمة (أو المؤشر نفسه في حالة تمرير المعامل بالمرجع)، لا يُضاف أي شيء إلى الواجهة (لأن القيمة قد نُسخت)، ولكنه يشير إلى أن الدالة لا تُعدّل النسخة المحلية من المعامل داخليًا (إنها عملية تعيين واحدة). لهذا السبب، يُفضّل البعض استخدام `in` constفي المعاملات فقط في حالة تمرير المعامل بالمرجع، حيث يُغيّر العقد، وليس في حالة تمرير المعامل بالقيمة، حيث يكشف عن تفاصيل التنفيذ.
لغة سي++
في لغة C++، هناك أربعة أنواع مختلفة من const:
constالثابت الزمني التقليديconstexpr، ثابت أو تعبير يمكن تقييمه في وقت الترجمة (انظر constexpr )consteval، والذي ينص على أن أي استدعاء لدالة يجب أن ينتج قيمة عبارة عن تعبير وقت الترجمةconstinit، مما يُعلن أن التعبير له تهيئة ثابتة (ثابتة)
طُرق
للاستفادة من أسلوب التصميم التعاقدي للأنواع المُعرَّفة من قِبل المستخدم (الهياكل والفئات)، والتي يمكن أن تحتوي على توابع بالإضافة إلى بيانات الأعضاء، يُمكن للمبرمج وسم توابع النسخ كما constلو أنها لا تُعدِّل بيانات أعضاء الكائن. constيُعد تطبيق المُؤهِّل على توابع النسخ ميزة أساسية لضمان صحة الثوابت، وهي غير متوفرة في العديد من لغات البرمجة الكائنية الأخرى مثل Java و C#، أو في C++/CLI من Microsoft أو Managed Extensions for C++ . بينما يُمكن استدعاء التوابع بواسطة الكائنات وغير الكائنات على حد سواء، لا يُمكن استدعاء توابع غير الكائنات إلا بواسطة كائنات غير الكائنات. يُطبَّق المُعدِّل على تابع النسخ على الكائن الذي يُشير إليه المؤشر " "، وهو وسيط ضمني يُمرَّر إلى جميع توابع النسخ. وبالتالي، فإن وجود التوابع هو طريقة لتطبيق صحة الثوابت على وسيط المؤشر " " الضمني تمامًا مثل الوسائط الأخرى.constconstconstconstconstconstthisconstthis
يوضح هذا المثال ما يلي:
class Integer { private : int i ; public : // لاحظ علامة const [[ nodiscard ]] int get () const noexcept { return i ; }// لاحظ عدم وجود "const" void set ( int j ) noexcept { i = j ; } };void foo ( Integer & nonConstInteger , const Integer & constInteger ) { int y = nonConstInteger . get (); // صحيح int x = constInteger . get (); // صحيح: get() ثابتnonConstInteger.set ( 10 ); // صحيح : nonConstInteger قابل للتعديل constInteger.set ( 10 ) ; // خطأ! set ( ) دالة غير ثابتة و constInteger كائن مؤهل بـ const }في الكود أعلاه، thisيكون المؤشر الضمني " " set()من النوع " Integer* const"؛ بينما thisيكون المؤشر " " من get()النوع " Integer const* const"، مما يشير إلى أن الطريقة لا يمكنها تعديل الكائن الخاص بها من خلال thisالمؤشر " ".
غالباً ما يُوفّر المبرمج في فئة واحدة نوعين من الدوال، إحداهما دالة عامة constوالأخرى constدالة غير عامة، تحملان الاسم نفسه (لكن قد تختلف استخداماتهما اختلافاً كبيراً) لتلبية احتياجات كلا النوعين من الدوال المُستدعِية. على سبيل المثال:
استيراد std ؛باستخدام std :: array ;class IntegerArray { private : array < int , 100 > data ; public : [[ nodiscard ]] int & get ( int i ) noexcept { return data [ i ]; }[[ nodiscard ]] int const & get ( int i ) const noexcept { return data [ i ]; } };void foo ( IntegerArray & a , const IntegerArray & ca ) { // الحصول على مرجع لعنصر مصفوفة // وتعديل قيمته المشار إليها.a.get ( 5 ) = 42 ; // صحيح! (استدعاءات: int& IntegerArray::get(int)) ca.get ( 5 ) = 42 ; // خطأ! (استدعاءات: int const& IntegerArray::get( int ) const ) }تحدد خاصية constالكائن المُستدعي أي إصدار من الدالة IntegerArray::get()سيتم استدعاؤه، وبالتالي ما إذا كان المُستدعي سيحصل على مرجع يمكنه من خلاله معالجة البيانات الخاصة في الكائن أو مجرد مراقبتها. من الناحية التقنية، يمتلك الأسلوبان توقيعين مختلفين لأن مؤشراتهما thisمن أنواع مختلفة، مما يسمح للمُصرّف باختيار النوع الصحيح. (قد يكون إرجاع constمرجع إلى كائن int، بدلاً من إرجاع الكائن intنفسه، مبالغة في الأسلوب الثاني، ولكن يمكن استخدام نفس الأسلوب مع أنواع عشوائية، كما هو الحال في مكتبة القوالب القياسية ).
ثغرات في صحة الثوابت
توجد عدة ثغرات في مبدأ صحة الثوابت المطلقة في لغتي C و C++. وهي موجودة أساسًا لتحقيق التوافق مع التعليمات البرمجية الموجودة.
الأولى، والتي تنطبق فقط على لغة C++، هي استخدام `const` const_cast، الذي يسمح للمبرمج بإزالة constالمُحدِّد، مما يجعل أي كائن قابلاً للتعديل. تبرز ضرورة إزالة المُحدِّد عند استخدام أكواد ومكتبات موجودة لا يمكن تعديلها ولكنها غير متوافقة مع `const`. على سبيل المثال، انظر إلى هذا الكود:
// نموذج أولي لدالة لا يمكننا تغييرها ولكننا نعلم أنها لا تُعدّل المؤشر المُمرَّر إليها. void myLibFunc ( int * p , int size );void callLibFunc ( const int * p , int size ) { myLibFunc ( p , size ); // خطأ! تم حذف مُحدِّد constint * nonConstPtr = const_cast < int *> ( p ); // إزالة المحدد myLibFunc ( nonConstPtr , size ); // صحيح }مع ذلك، فإن أي محاولة لتعديل كائن مُعرَّف constباستخدام تحويل ثابت (const cast) تُؤدي إلى سلوك غير مُعرَّف وفقًا لمعيار ISO C++. في المثال أعلاه، إذا كان `const ptrcast` يُشير إلى متغير عام أو محلي أو عضو مُعرَّف كـ `const constcast`، أو كائن مُخصَّص في الذاكرة الديناميكية (heap) باستخدام `const cast` new int const، فإن الكود يكون صحيحًا فقط إذا LibraryFuncلم يُعدِّل `const cast` القيمة التي يُشير إليها `const cast` ptr.
تحتاج لغة C إلى ثغرة قانونية بسبب وجود حالة معينة. يُسمح بتعريف المتغيرات ذات مدة التخزين الثابتة بقيمة ابتدائية. مع ذلك، لا يمكن للمُهيئ استخدام سوى الثوابت، مثل ثوابت السلاسل النصية والقيم الحرفية الأخرى، ولا يُسمح له باستخدام عناصر غير ثابتة، مثل أسماء المتغيرات، سواءً أُعلنت عناصر المُهيئ constأم لا، أو أُعلن عن متغير مدة التخزين الثابتة نفسه constأم لا. توجد طريقة غير قابلة للنقل لتهيئة constمتغير ذي مدة تخزين ثابتة. من خلال إنشاء تحويل نوع دقيق على الجانب الأيسر من عملية إسناد لاحقة، constيمكن الكتابة إلى المتغير، مما يؤدي فعليًا إلى إزالة constالخاصية و"تهيئته" بعناصر غير ثابتة، مثل constمتغيرات أخرى وما شابه. constقد تعمل الكتابة إلى متغير بهذه الطريقة كما هو مُتوقع، لكنها تُسبب سلوكًا غير مُحدد وتُخالف بشكلٍ خطير مبدأ صحة الثوابت.
constexpr size_t BUFFER_SIZE = 8 * 1024 ; const size_t userTextBufferSize ; // تعتمد القيمة الأولية على const BUFFER_SIZE، ولا يمكن تهيئتها هنا...int setupUserTextBox ( TextBox * defaultTextBoxType , Rectangle * defaultTextBoxLocation ) { * ( size_t * ) & userTextBufferSize = BUFFER_SIZE - sizeof ( TextBoxControls ); // تحذير: قد يعمل، لكن ليس مضمونًا من قِبل لغة C ... }توجد ثغرة أخرى [ 12 ] تنطبق على كلٍ من لغتي C وC++. تحديدًا، تنص اللغتان على أن مؤشرات الأعضاء والمراجع "سطحية" فيما يتعلق بخصائص constمالكيها - أي أن الكائن الحاوي الذي constيحتوي على جميع constالأعضاء باستثناء الأعضاء التي تشير إليها (والمراجع) لا يزال قابلاً للتغيير. للتوضيح، انظر إلى كود C++ التالي:
struct MyStruct { int val ; int * ptr ; };void foo ( const MyStruct & s ) { int i = 42 ; s . val = i ; // خطأ: s ثابت، لذا فإن val عدد صحيح ثابت int s . ptr = & i ; // خطأ: s ثابت، لذا فإن ptr مؤشر ثابت إلى int * s . ptr = i ; // صحيح: البيانات التي يشير إليها ptr قابلة للتغيير دائمًا، على الرغم من أن هذا غير مرغوب فيه أحيانًا }على الرغم من أن الكائن sالمُمرَّر foo()ثابت، مما يجعل جميع عناصره ثابتة، إلا أن الكائن المُشار إليه (الذي يمكن الوصول إليه عبر هذا الكائن) s.ptrيظل قابلاً للتعديل، مع أن هذا قد لا يكون مرغوبًا من منظور constصحة الكود، لأن sالكائن قد يمتلك الكائن المُشار إليه حصريًا. لهذا السبب، يرى مايرز أن الوضع الافتراضي لمؤشرات العناصر والمراجع يجب أن يكون "عميقًا" const، والذي يمكن تجاوزه باستخدام mutableمُحدِّد عندما لا يكون الكائن المُشار إليه مملوكًا للحاوية، لكن هذه الاستراتيجية ستُسبب مشاكل توافق مع التعليمات البرمجية الموجودة. لذا، ولأسباب تاريخية ، تبقى هذه الثغرة مفتوحة في لغتي C وC++.
يمكن سد الثغرة الأخيرة باستخدام فئة لإخفاء المؤشر خلف constواجهة صحيحة، ولكن هذه الفئات إما لا تدعم دلالات النسخ المعتادة من constكائن (مما يعني أنه لا يمكن نسخ الفئة الحاوية بالدلالات المعتادة أيضًا) أو تسمح بثغرات أخرى من خلال السماح بإزالة خاصية const-ness من خلال النسخ غير المقصود أو المتعمد.
أخيرًا، تنتهك العديد من الدوال في مكتبة C القياسية مبدأ صحة الثوابت قبل إصدار C23 ، حيث تقبل constمؤشرًا إلى سلسلة نصية وتعيد قيمة غير constمؤشرية إلى جزء من السلسلة نفسها. strstrومن strchrبين هذه الدوال، تُعدّ `const` و`const` من بين الدوال التي تنتهك هذا المبدأ. تحاول بعض تطبيقات مكتبة C++ القياسية، مثل تطبيق مايكروسوفت [ 13 ] ، سدّ هذه الثغرة بتوفير نسختين مُحمّلتين بشكل زائد من بعض الدوال: constنسخة "const" ونسخة "const const".
مشاكل
يؤدي استخدام نظام الأنواع للتعبير عن الثبات إلى تعقيدات ومشاكل عديدة، ولذلك تعرض لانتقادات ولم يُعتمد خارج نطاق عائلة لغات C الضيقة، C وC++ وD. أما لغتا Java وC#، المتأثرتان بشدة بـ C وC++، فقد رفضتا صراحةً constمُحدِّدات الأنواع من النمط -، واستبدلتاها بالتعبير عن الثبات باستخدام كلمات مفتاحية تُطبَّق على المُعرِّف ( finalفي Java، constو readonlyفي C#). حتى داخل لغتي C وC++، يختلف استخدام - constبشكل كبير، فبعض المشاريع والمؤسسات تستخدمه باستمرار، بينما تتجنبه أخرى.
strchrمشكلة
يُسبب مُحدد النوع constصعوبات عندما لا يُبالي منطق الدالة بما إذا كان مُدخلها ثابتًا أم لا، ولكنه يُعيد قيمةً يجب أن تكون من نفس النوع المُحدد للمُدخل. بعبارة أخرى، بالنسبة لهذه الدوال، إذا كان المُدخل ثابتًا (مُحددًا بـ const)، فيجب أن تكون القيمة المُعادة كذلك، وإذا كان المُدخل متغيرًا (غير constمُحدد بـ const)، فيجب أن تكون القيمة المُعادة كذلك. ولأن توقيع النوع لهذه الدوال يختلف، فإن ذلك يتطلب دالتين (أو أكثر في حالة وجود مُدخلات متعددة) بنفس المنطق - وهو شكل من أشكال البرمجة العامة .
تظهر هذه المشكلة حتى مع الدوال البسيطة في مكتبة C القياسية ، ولا سيما دالة ` fl strchr. ...strchrcs
char * strchr ( char * s , int c );لا تقوم الدالة strchrبتعديل سلسلة الإدخال، ولكن غالبًا ما يستخدم المستدعي القيمة المُعادة لتعديل السلسلة، مثل:
إذا كان ( p = strchr ( q , '/' )) { * p = ' ' ; }وبالتالي، من ناحية، يمكن أن تكون سلسلة الإدخال const(لأنها لا يتم تعديلها بواسطة الدالة)، وإذا كانت سلسلة الإدخال كذلك، constفيجب أن تكون قيمة الإرجاع كذلك أيضًا - ببساطة لأنها قد تُرجع مؤشر الإدخال بالضبط، إذا كان الحرف الأول مطابقًا - ولكن من ناحية أخرى، لا ينبغي أن تكون قيمة الإرجاع كذلك constإذا لم تكن السلسلة الأصلية كذلك const، لأن المستدعي قد يرغب في استخدام المؤشر لتعديل السلسلة الأصلية.
في لغة C++، يتم ذلك من خلال تحميل الدوال الزائد ، والذي يتم تنفيذه عادةً عبر قالب ، مما ينتج عنه دالتان، بحيث يكون للقيمة المرجعة نفس constالنوع المؤهل للإدخال:
char * strchr ( char * s , int c ); const char * strchr ( const char * s , int c );ويمكن تعريف هذه بدورها بواسطة قالب:
template < T > T * strchr ( T * s , int c ) { ... }في لغة D، يتم التعامل مع هذا عبر الكلمة inoutالمفتاحية، والتي تعمل كحرف بدل لـ const أو immutable أو unqualified (variable)، مما ينتج عنه: [ 15 ] [ b ]
inout ( char )* strchr ( inout ( char )* s , int c );ومع ذلك، في لغة C، لا يمكن تحقيق أي من هذين الأمرين [ c ] لأن لغة C لا تحتوي على تحميل زائد للدوال، وبدلاً من ذلك، يتم التعامل مع هذا الأمر من خلال وجود دالة واحدة يكون فيها المدخل ثابتًا ولكن المخرج قابل للكتابة:
char * strchr ( const char * s , int c );يسمح هذا بكتابة كود C متوافق مع قواعد اللغة، ولكنه يُزيل مُحدِّد const إذا كان المُدخل مُؤهَّلاً بالفعل بـ const، مما يُخالف قواعد سلامة النوع . اقترح ريتشي هذا الحل، وتم اعتماده لاحقًا. يُعد هذا الاختلاف أحد أوجه قصور التوافق بين لغتي C و C++ .
منذ الإصدار C23 ، تم حل هذه المشكلة باستخدام ميزة _Genericاللغة: strchrحيث تم تحويل مُعرّفات الدوال الأخرى المتأثرة بهذه المشكلة إلى وحدات ماكرو تُوسّع استدعاء دالة مناسبة تُعيد constمؤشرًا إذا تم تمرير مؤشر إليها، ومؤشرًا غير مؤهل إذا تم تمرير مؤشر غير مؤهل إليها. [ 16 ]
د
في الإصدار الثاني من لغة البرمجة D ، توجد كلمتان مفتاحيتان تتعلقان بالثوابت (const). [ 17 ] تشير الكلمة immutableالمفتاحية الأولى إلى بيانات لا يمكن تعديلها من خلال أي مرجع. constوتشير الكلمة المفتاحية الثانية إلى عرض غير قابل للتغيير لبيانات قابلة للتغيير. على عكس لغة C++ const، فإن الثوابت في D constو immutableهما "عميقة" أو متعدية ، وأي شيء يمكن الوصول إليه من خلال كائن `const` constأو ` immutableconst` هو const`const` أو `const` immutableعلى التوالي.
مثال على الفرق بين الثابت وغير القابل للتغيير في لغة D
int [] foo = new int [ 5 ]; // foo قابلة للتغيير. const int [] bar = foo ; // bar عبارة عن عرض ثابت لبيانات قابلة للتغيير. immutable int [] baz = foo ; // خطأ: يجب أن تكون جميع عروض البيانات غير القابلة للتغيير غير قابلة للتغيير.immutable int [] nums = new immutable ( int )[ 5 ]; // لا يمكن إنشاء مرجع قابل للتغيير إلى nums. const int [] constNums = nums ; // يعمل. immutable قابل للتحويل ضمنيًا إلى const. int [] mutableNums = nums ; // خطأ: لا يمكن إنشاء عرض قابل للتغيير لبيانات غير قابلة للتغيير.مثال على الثابت المتعدي أو الثابت العميق في لغة D
class LinkedList { int value ; LinkedList next ; }`immutable LinkedList ll = new immutable ( LinkedList ); ll.next.value = 5 ; ` // لن يتم تجميع الكود. `ll.next` من النوع `immutable(LinkedList)`. // ` ll.next.value` من النوع `immutable(int)` .تاريخ
constتم تقديمها بواسطة بيارن ستروستروب في لغة C مع الفئات ، وهي اللغة السابقة للغة C++ ، في عام 1981، وكان اسمها الأصلي readonly. [ 18 ] [ 19 ] أما عن الدافع، فقد كتب ستروستروب: [ 19 ]
- "لقد خدمت وظيفتين: كوسيلة لتعريف ثابت رمزي يلتزم بقواعد النطاق والنوع (أي بدون استخدام ماكرو) وكطريقة لاعتبار كائن في الذاكرة غير قابل للتغيير."
كان الاستخدام الأول، كبديل محدد النطاق والنوع للوحدات البرمجية، قد تم تحقيقه بشكل مماثل للوحدات البرمجية الشبيهة بالدوال عبر الكلمة المفتاحية. وقد اقترح دينيس ريتشي inlineالمؤشرات الثابتة والترميز وتم اعتمادهما. [ 19 ]* const
constثم اعتُمدت هذه الخاصية في لغة C كجزء من عملية التوحيد القياسي، وتظهر في C89 (والإصدارات اللاحقة) إلى جانب مُحدِّد النوع الآخر volatile. [ 20 ] واقتُرح مُحدِّد آخر، ، noaliasفي اجتماع لجنة X3J11 في ديسمبر 1987، لكنه رُفض؛ وتحقق هدفه في نهاية المطاف من خلال الكلمة restrictالمفتاحية في C99 . لم يكن ريتشي مؤيدًا بشدة لهذه الإضافات، بحجة أنها لا تُحقق الغرض منها، لكنه في النهاية لم يُطالب بإزالتها من المعيار. [ 21 ]
ورثت لغة D لاحقًا constمن لغة C++، حيث تُعرف باسم مُنشئ النوع (وليس مُؤهِّل النوع )، وأضافت مُنشئَي نوع آخرين، immutableو inout، للتعامل مع حالات الاستخدام ذات الصلة. [ d ]
لغات أخرى
لا تتبع لغات البرمجة الأخرى لغة C/C++ في اعتبار الثبات جزءًا من النوع، على الرغم من أنها غالبًا ما تمتلك بنى متشابهة ظاهريًا وقد تستخدم الكلمة constالمفتاحية. وعادةً ما يُستخدم هذا فقط للثوابت (الكائنات الثابتة).
تحتوي لغة C#const على كلمة مفتاحية، ولكن بدلالات مختلفة تمامًا وأبسط: فهي تعني ثابتًا وقت الترجمة، وليست جزءًا من النوع. وهي مكافئة بشكل أساسي لـ constexprفي لغتي C و C++.
تحتوي لغة Nimconst على كلمة مفتاحية مشابهة لتلك الموجودة في لغة C#: فهي تُعلن عن ثابت وقت الترجمة بدلاً من أن يكون جزءًا من النوع. مع ذلك، في Nim، يمكن الإعلان عن ثابت من أي تعبير يمكن تقييمه وقت الترجمة. [ 22 ] في C#، لا يمكن الإعلان عن أنواع C# المضمنة إلا كـ `static` const؛ أما الأنواع المعرفة من قِبل المستخدم، بما في ذلك الفئات والهياكل والمصفوفات، فلا يمكن تعريفها كـ `static` const. [ 23 ]
تحتوي لغة Rustconst على كلمة مفتاحية مشابهة لتلك الموجودة في لغة C#، والتي تُعلن عن الثوابت والدوال والرموز الأخرى في وقت الترجمة. [ 24 ] وبالتالي فهي تُعادل بشكل أساسي ما هو constexprموجود في لغتي C و C++.
لا تستخدم لغة جافاconst `<C++>`، بل تستخدم `<C++>` final، والتي يمكن تطبيقها على تعريفات "المتغيرات" المحلية، وتُطبق على المعرّف وليس النوع. ولها استخدام مختلف في البرمجة الكائنية لأعضاء الكائن، وهو أصل التسمية. تعتبر مواصفات لغة جافا `<C++>` constكلمة محجوزة - أي لا يمكن استخدامها كمعرّف متغير - لكنها لا تُسند إليها أي دلالات: فهي كلمة محجوزة (لا يمكن استخدامها في المعرّفات) ولكنها ليست كلمة مفتاحية (ليس لها معنى خاص). أُدرجت الكلمة المفتاحية `<C++>` كوسيلة لمترجمات جافا لاكتشاف الاستخدام غير الصحيح لكلمات C++ المفتاحية والتحذير منه. [ 25 ] يوجد طلب تحسين لتنفيذ constهذه الخاصية في عملية مجتمع جافا ، ولكن تم إغلاقه في عام 2005 على أساس أنه من المستحيل تنفيذه بطريقة متوافقة مع الإصدارات السابقة. [ 26 ]
كان للغة Ada 83 المعاصرة مفهوم الكائن الثابت والكلمة constantالمفتاحية، [ 27 ] [ e ] حيث كانت معلمات الإدخال ومعلمات الحلقة ثابتة ضمنيًا. هنا، constantتُعدّ خاصية الكائن، وليست خاصية النوع.
تحتوي لغة جافا سكريبتconst على تعريف يُحدد متغيرًا بنطاق كتلة لا يمكن إعادة تعيينه أو إعادة تعريفه. يُحدد هذا التعريف مرجعًا للقراءة فقط لمتغير لا يمكن إعادة تعريفه، ولكن في بعض الحالات قد تتغير قيمة المتغير نفسه، كما لو كان المتغير يُشير إلى كائن وتم تغيير إحدى خصائصه. [ 28 ]
انظر أيضاً
ملحوظات
- ↑ رسميًا عندما
constيكون جزءًا من النوع المشتق الخارجي في الإعلان؛ المؤشرات تعقد النقاش. - ↑ في لغة البرمجة D الاصطلاحية، يُستخدم هنا مصفوفة بدلاً من مؤشر. [ 15 ]
- ↑ في C11 وما بعدها
_Genericكان من الممكن استخدامها لتنفيذ const-correctstrchr.char * strchr_m ( char * s , int c ); const char * strchr_c ( const char * s , int c ); #define strchr(X,Y) _Generic((X), \ char*: strchr_m, \ const char*: strchr_c \ )(X,Y)
- ↑ قدمت D أيضًا
sharedمُنشئ النوع، لكن هذا يتعلق بحالات استخدامvolatile، وليسconst. - ↑ يُطلق معيار Ada على هذا اسم " كلمة محجوزة "؛ انظر تلك المقالة للاطلاع على الاستخدام.
مراجع
- ↑ "العناصر الثابتة - مرجع لغة رست" . doc.rust-lang.org . تم الاطلاع عليه بتاريخ 22-06-2022 .
- ↑ " المؤشر" . مسودة معيار C++ . تم الاطلاع عليه بتاريخ 30-03-2020 .
نوع المؤشر
في دالة عضوية يكون نوعها يحتوي على
cv-qualifier-seq cv
وفئتها
هي "مؤشر إلى
cv
".
thisthisXX - ↑ هيرب ساتر وأندريه ألكسندرسكو (2005). معايير برمجة لغة C++ . ص 30. بوسطن: أديسون ويسلي. ISBN 0-321-11358-6
- ↑ "لماذا يكون وسيط الدالة kfree() ثابتًا؟" . lkml.org. 2013-01-12.
- ↑ "5.1. الإضافات المُطبقة في لغة جنو فورتران: 5.1.16 مؤشرات كراي". مُترجم جنو فورتران . 2006. مؤرشف من الأصل بتاريخ 21-12-2022 . تم الاطلاع عليه بتاريخ 21-12-2022 .
- ↑ فاهي، مارك ر.؛ ناغل، دان (1999-04-19). "مؤشرات كراي فورتران مقابل مؤشرات فورتران 90 ونقل البيانات من كراي C90 إلى SGI Origin2000" (ملف PDF) . فيكسبيرغ، ماساتشوستس، الولايات المتحدة الأمريكية: محطة تجارب الممرات المائية التابعة لفيلق مهندسي الجيش الأمريكي، مركز الموارد المشتركة الرئيسي. مؤرشف (ملف PDF) من الأصل بتاريخ 2022-12-23 . تم الاطلاع عليه بتاريخ 2022-12-23 .(8 صفحات)
- ↑ "الملحق ج: ميزات واختلافات فورتران 90 > الميزات > مؤشرات كراي" . دليل مستخدم فورتران . شركة أوراكل . 2010. مؤرشف من الأصل بتاريخ 21-09-2021 . تم الاطلاع عليه بتاريخ 23-12-2022 .
- ↑ "الملحق ج: ميزات واختلافات فورتران 90 > الميزات > مؤشرات أحرف كراي" . دليل مستخدم فورتران . شركة أوراكل . 2010. مؤرشف من الأصل بتاريخ 23-12-2022 . تم الاطلاع عليه بتاريخ 23-12-2022 .
- ↑ "الفصل 4. أنواع البيانات". دليل مرجع لغة فورتران، المجلد 1. المجلد 1. شركة سيليكون جرافيكس، 1999 [1993]. رقم الوثيقة: 007-3692-004. مؤرشف من الأصل بتاريخ 23-12-2022 . تم الاطلاع عليه بتاريخ 23-12-2022 . (ملاحظة: مقتبس من "FORTRAN 90 HANDBOOK" (1992، McGraw-Hill, Inc. ) بقلم والتر إس. برينرد، وجين سي. آدامز، وجين تي. مارتن، وبريان تي. سميث، وجيرولد إل. واغنر.)
- ↑ "التعليمات البرمجية غير الآمنة، وأنواع المؤشرات، ومؤشرات الدوال" . learn.microsoft.com . مايكروسوفت ليرن. 2025-02-06.
- ↑ "Stroustrup: C++ Style and Technique FAQ" .
- ↑ سكوت مايرز (2005). لغة C++ الفعالة، الطبعة الثالثة . الصفحات 21-23. بوسطن: أديسون ويسلي. ISBN 978-0-321-33487-9
- ↑ "strchr, wcschr, _mbschr (CRT)" . Msdn.microsoft.com . تم الاطلاع عليه بتاريخ 23-11-2017 .
- ↑ "دينيس ريتشي: لماذا لا أحب مؤهلات نوع X3J11" .
- 1 2 لغة البرمجة D، أندريه ألكسندرسكو ، 8.8: نقل مُؤهِّل من المُعامل إلى النتيجة
- ↑ "WG14-N3020 : دوال المكتبة القياسية التي تحافظ على المُؤهِّل" (ملف PDF) . open-std.org . 13-06-2022. مؤرشف (ملف PDF) من الأصل بتاريخ 13-10-2022.
- ↑ "const(FAQ) – لغة البرمجة D" . Digitalmars.com . تم الاطلاع عليه بتاريخ 18-08-2013 .
- ↑ بيارن ستروستروب ، "امتدادات مفهوم نوع لغة C"، مذكرة فنية داخلية لمختبرات بيل، 5 يناير 1981.
- 1 2 3 التنافس بين الأشقاء: لغتا C و C++ ، بيارن ستروستروب ، 2002، ص 5
- ↑ دينيس م. ريتشي ، " تطوير لغة C" (رابط قديم مؤرشف في 15 يوليو 2012، على archive.today )، 2003: "قدمت X3J11 أيضًا مجموعة من الإضافات والتعديلات الصغيرة، على سبيل المثال، محددات النوع const و volatile ، وقواعد ترقية النوع المختلفة قليلاً."
- ↑ دعوني أبدأ بالقول إنني لست مقتنعًا بأن حتى المحددات التي كانت سائدة قبل ديسمبر ('const' و'volatile') تؤدي الغرض منها؛ أظن أن ما تضيفه إلى تكلفة تعلم اللغة واستخدامها لا يُعوَّض بزيادة في التعبير. 'volatile' تحديدًا، هي مجرد إضافة غير ضرورية للتطبيقات المتخصصة، ويمكن التعبير عنها بشكل أفضل بوسائل أخرى. ميزتها الرئيسية هي أن الجميع تقريبًا يمكنهم تجاهلها. أما 'const' فهي في الوقت نفسه أكثر فائدة وأكثر وضوحًا؛ لا يمكنك تجنب تعلمها، نظرًا لوجودها في واجهة المكتبة. مع ذلك، لا أدعو إلى استئصال المحددات، ولو فقط لأنه قد فات الأوان.
- ↑ دليل لغة نيم: قسم الثوابت
- ↑ const (مرجع C#)
- ↑ "const - Rust" . doc.rust-lang.org . تم الاطلاع عليه بتاريخ 2025-09-01 .
- ↑ جوسلينج، جيمس؛ جوي، بيل؛ ستيل، جاي. "مواصفات لغة جافا، الطبعة الثالثة" .
- ↑ "معرف الخطأ: JDK-4211070، يجب أن تدعم لغة جافا المعاملات الثابتة (مثل لغة C++) لصيانة الكود [ sic ] " . Bugs.sun.com . تم الاطلاع عليه بتاريخ 2014-11-04 .
- ↑ 1815أ3.2.1 . تعريفات الكائنات (مؤرشفة في 20 أكتوبر 2014 على موقع Wayback Machine ): "يُعتبر الكائن المُعلن عنه ثابتًا إذا ظهرت الكلمة المحجوزة constant في تعريف الكائن؛ ويجب أن يتضمن التعريف حينها تهيئة صريحة. لا يمكن تعديل قيمة الثابت بعد التهيئة. تُعتبر المعاملات الرسمية للوضع in للبرامج الفرعية والإدخالات، والمعاملات الرسمية العامة للوضع in، ثوابت أيضًا؛ كما يُعتبر مُعامل الحلقة ثابتًا داخل الحلقة المقابلة؛ ويُعتبر المُكوّن الفرعي أو شريحة الثابت ثابتًا."
- ↑ "const" . MDN . تم الاسترجاع في 31-10-2017 .
روابط خارجية
- "الدقة الثابتة" بقلم هيرب سوتر
- "التحسين المستمر؟" بقلم هيرب سوتر
- الأسئلة الشائعة المختصرة حول لغة C++: صحة الثوابت بقلم مارشال كلاين
- القسم " استبدال القيم " من الكتاب الإلكتروني المجاني " التفكير بلغة C++" للمؤلف بروس إيكل
- "هنا شرطي، وهناك شرطي" بقلم والتر برايت
- "الثوابت والثوابت" من مواصفات لغة البرمجة D، الإصدار 2 (تجريبي)
- عائلة لغات البرمجة C
- أنواع البيانات
- مواضيع لغات البرمجة
