C++ / الأساسي / الدرس 2
القيم والأنواع والتهيئة الآمنة
اختر تمثيل البيانات بوعي، وهيّئ كل قيمة محلية، وافهم الحساب دون افتراض تخطيط محدّد للآلة.
في هذه الصفحة
أهداف الدرس
- تمييز القيم الصحيحة والعشرية والمنطقية والمحارف.
- استخدام التهيئة بالأقواس المعقوفة وكشف التحويلات المضيّقة.
- شرح القسمة والفيض وconst وconstexpr في C++20.
النوع مجموعة قواعد
يسمّي المتغيّر كائنًا له نوع وتخزين وعمر. يمثّل int مجالًا محدودًا من الأعداد الصحيحة، ويقارب double الكميات الحقيقية، ويمثّل bool الصواب والخطأ. لا ترمّز هذه الأنواع وحدات القياس تلقائيًا. اختر اسمًا واضحًا مثل duration_seconds بدل انتظار أن يشرح العدد نفسه. لا تشترط C++ أن يكون int من اثنين وثلاثين بتًا تمامًا. استخدم numeric_limits حين يهم المجال، واختر الأسماء الصحيحة ذات العرض الثابت فقط عندما تناسب إتاحتها الاختيارية وعرضها تنسيقًا خارجيًا.
إعطاء قيمة عند التصريح
اكتب int count{} للحصول على صفر أو int count{7} لقيمة معلومة. لا يملك المتغيّر المحلي int count; قيمة مهيأة صالحة لمجرد وجود تخزين؛ وقراءته في حساب عادي سلوك غير معرّف في C++20. ترفض الأقواس المعقوفة كثيرًا من التحويلات المضيّقة، مثل int count{2.5}. تمنع const إسنادًا لاحقًا إلى الكائن من واجهته المعتادة. وتلزم constexpr أيضًا بتهيئة ثابتة للمتغيّر. لا تحوّل أي منهما الإدخال الخارجي غير المفحوص إلى قيمة صالحة، لذا تبقى فحوص التمثيل عند الحدود ضرورية.
القسمة تعتمد على نوعي المعاملين
تقطع القسمة ذات المعاملين الصحيحين باتجاه الصفر، وتكمل البقية العلاقة: المقسوم يساوي خارج القسمة مضروبًا في المقسوم عليه زائد البقية عندما تكون العملية قابلة للتمثيل. لا يعيد إسناد خارج صحيح إلى double الجزء الكسري المحذوف. حوّل أحد المعاملين قبل القسمة إذا أردت كسرًا. يطبع المثال الأول التفسيرين عمدًا. القيم صغيرة وممثلة بدقة هنا، لذلك يثبت الإخراج المعروض مع التنسيق الافتراضي للتدفق. وقد تتطلب حسابات عشرية عامة سياسة تقريب صريحة.
#include <iostream>
int main() {
const int pages{7};
const int readers{2};
std::cout << pages / readers << " " << pages % readers << "\n";
std::cout << static_cast<double>(pages) / readers << "\n";
}
3 1
3.5
تعطي القسمة الصحيحة ثلاثة وبقية واحد؛ وتحويل pages قبل القسمة يعطي ثلاثة ونصفًا.
إظهار قصد التحويل
يبيّن التحويل الصريح نية المبرمج ولا يثبت سلامته. يحذف تحويل قيمة عشرية إلى عدد صحيح جزأها الكسري، والنتيجة الخارجة عن المجال ليست تحويلًا محمولًا صالحًا. افحص المجال وشروط المسألة قبل تحويل القيم الخارجية. تستنتج auto النوع من المهيئ، ولا تعني نوعًا يتغير ديناميكيًا. في البرنامج الثاني يبقى total من نوع int ويكون average من نوع double لأن التعبير يحتوي على معامل double. يطلب boolalpha طباعة القيم المنطقية بالكلمات.
#include <iostream>
int main() {
constexpr int total{18};
const auto average = total / 4.0;
const bool enough = average >= 4.0;
std::cout << average << " " << std::boolalpha << enough << "\n";
}
4.5 true
يجعل المعامل العشري المتوسط 4.5، وتنتج المقارنة true دون تغيير القيمة العددية.
احترام حدود المجال
فيض الأعداد الصحيحة ذات الإشارة والقسمة الصحيحة على صفر سلوكان غير معرّفين، فلا ناتج مطبوعًا مضمونًا لهما. تلتف الأعداد غير ذات الإشارة وفق مجال تمثيلها، وهو سلوك معرّف لكنه غالبًا غير مناسب لفاتورة أو عدّاد. قد يحوّل المزج بين عدد ذي إشارة وآخر دون إشارة القيمة السالبة إلى قيمة كبيرة قبل المقارنة. وحّد المجال العددي وافحص العدّ قبل التحويل، وصغ فحوص الفيض دون إجراء العملية الخطرة داخل الفحص. وتتطلب مساواة القيم العشرية أيضًا تبريرًا لدقة المسألة.
الأخطاء الشائعة وإصلاحها
متوسط يساوي ثلاثة على نحو غير متوقع.
- لماذا يحدث؟
- معاملا القسمة كلاهما صحيحان.
- كيف تصلحه؟
- حوّل معاملًا قبل القسمة بعد فحص المقام.
- كيف تتجنب تكراره؟
- راجع نوعي المعاملين قبل اختيار نوع متغيّر الناتج.
عدّاد يطبع قيمًا غير متسقة.
- لماذا يحدث؟
- تُقرأ قيمة محلية قياسية غير مهيأة.
- كيف تصلحه؟
- هيّئها بالأقواس المعقوفة قبل أي قراءة.
- كيف تتجنب تكراره؟
- فعّل التحذيرات وهيّئ المتغيّر عند التصريح.
مجموع كبير يبدو أنه أصبح سالبًا.
- لماذا يحدث؟
- تجاوز الحساب ذو الإشارة مجال تمثيله.
- كيف تصلحه؟
- افحص المعاملين باستخدام numeric_limits قبل الجمع.
- كيف تتجنب تكراره؟
- حدّد الحدود العددية ولا تعتمد على نتيجة فيض مرصودة.
اختبر فهمك
1. لأحد عشر عملًا توزّع على أربعة عمال، احسب الحصة الصحيحة والبقية والمتوسط الرياضي، واشرح أنواع المعاملات.
اعرض الحل المشروح
مع معاملين من int تكون 11 / 4 مساوية 2 و11 % 4 مساوية 3. يعطي تحويل البسط static_cast<double>(11) / 4 القيمة 2.75. ويبقى إسناد 11 / 4 إلى متغيّر double مساويًا 2 لأن القسمة الصحيحة سبقت الإسناد. افحص أن المقام ليس صفرًا عند تعميم الحل على الإدخال.
2. صمّم زيادة آمنة لعدّاد int غير سالب قد يساوي قيمته العظمى. صف النتيجتين دون تنفيذ فيض.
اعرض الحل المشروح
قارن counter مع std::numeric_limits<int>::max() من الترويسة limits. إذا ساواه فأبلغ عن تجاوز السعة واتركه دون تغيير، وإلا فزده مرة واحدة. لا تنفّذ المقارنة جمعًا خطرًا؛ أما فحص counter + 1 لاحقًا فيكون قد نفّذ العملية التي قد تكون غير معرّفة. اختبر الصفر والقيمة العظمى صراحة.
أهم ما تعلمته
- هيّئ القيم وارفض التضييق ما لم يكن التحويل مبررًا.
- تحدّد أنواع المعاملات الحساب قبل الإسناد.
- لا يملك السلوك غير المعرّف عقد إخراج موثوقًا.