Menu
flag Ar iconالعربيةdown icon

حلقة for في C++: الصياغة والأمثلة والأخطاء الشائعة

كيفية تكرار الشيفرة باستخدام حلقة for في C++ - الترويسة المكوّنة من ثلاثة أجزاء، العدّ تصاعديًا وتنازليًا، المرور على المصفوفات، التداخل، break وcontinue، وأخطاء الانزياح بمقدار واحد والأنواع غير الموقّعة التي تطال الجميع.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

لماذا حلقة for

تختار جملة switch فرعًا واحدًا لتشغيله مرة واحدة. لكن البرامج الحقيقية تحتاج إلى فعل شيء بشكل متكرر: طباعة كل درجة، جمع قائمة من الأرقام، رسم 10 صفوف من شبكة. حلقة for هي حصان العمل في C++ لتكرار الشيفرة عددًا معروفًا من المرات، مع عدّاد مدمج تتحكم به.

كل ما تحتاجه حلقة for يقع في ترويسة واحدة موجزة، بحيث تبقى قصة "كم مرة وكيف" بأكملها مرئية بنظرة واحدة.

الترويسة المكوّنة من ثلاثة أجزاء

تتكوّن ترويسة حلقة for من ثلاثة أجزاء مفصولة بفواصل منقوطة: مُهيِّئ، وشرط، وتحديث.

for (initializer; condition; update) {
    // الجسم - يُنفَّذ ما دام الشرط صحيحًا
}

تُنفَّذ هذه الأجزاء بترتيب محدّد: يُنفَّذ المُهيِّئ مرة واحدة في البداية؛ ثم يُفحَص الشرط قبل كل تكرار؛ ويُنفَّذ الجسم فقط إذا كان الشرط true؛ ويُنفَّذ التحديث في نهاية كل تكرار، قبل فحص الشرط مجددًا مباشرة.

هنا يُنفَّذ int i = 0 مرة واحدة. ثم يُفحَص i < 5: ما دام محقّقًا، يطبع الجسم ويزيد i++ العدّاد. عندما يبلغ i القيمة 5 يصبح الشرط false، فتخرج الحلقة ويُطبَع done. يُنفَّذ الجسم 5 مرات بالضبط، حيث تأخذ i القيم من 0 إلى 4.

تعريف العدّاد داخل الترويسة (int i = 0) يجعل نطاق i محصورًا في الحلقة - فهو لا يوجد بعد القوس المعقوف الخاتم، وهو بالضبط ما تريده.

العدّ تصاعديًا وتنازليًا وبخطوات

جزء التحديث ليس مقصورًا على i++. يمكنك العدّ تنازليًا، أو التقدّم بأي مقدار، أو المرور على فهرس مصفوفة.

تُنفَّذ الحلقة الأولى ما دام i > 0، وتُنقص في كل مرة، فتطبع 5 4 3 2 1. أما الثانية فتضيف 2 في كل مرور وتستخدم <= 10 لأن 10 قيمة نريد تضمينها. وافِق بين شرطك وتحديثك: العدّ التنازلي يقترن بـ > أو >=، والعدّ التصاعدي بـ < أو <=.

المرور على مصفوفة

أكثر استخدامات حلقة العدّ شيوعًا هو المرور على مصفوفة بالفهرس. يؤدّي العدّاد أيضًا دور الموضع الذي تقرؤه.

لاحظ أن الشرط هو i < n، وليس i <= n. مصفوفة من 5 عناصر فهارسها الصالحة من 0 إلى 4؛ والفهرس 5 يقع بعد النهاية. قراءة scores[5] سلوك غير معرَّف - قد تطبع قيمًا مهملة، أو تتعطّل، أو تبدو وكأنها تعمل بينما تُفسد الذاكرة بصمت. نمط i < n هو الخيار الافتراضي الآمن لأي مصفوفة تبدأ من الصفر.

إن كنت تحتاج القيم فقط دون الفهرس، فإن حلقة for المبنية على نطاق أنظف. لجأ إلى الحلقة المفهرسة الكلاسيكية عندما تحتاج الموضع فعلًا.

break و continue

كلمتان مفتاحيّتان تتيحان لك تغيير المسار في منتصف الحلقة. تخرج break من الحلقة فورًا؛ وتتخطّى continue بقية التكرار الحالي وتقفز إلى التحديث.

تتوقّف الحلقة الأولى لحظة عثورها على 7 ولا تفحص الباقي أبدًا. وتستخدم الثانية continue لتخطّي طباعة الجسم كلما كان i زوجيًا - ومع ذلك يُنفَّذ التحديث i++، فتواصل الحلقة التقدّم. مزلق دقيق: تقفز continue إلى التحديث، لذا إن اعتمدت يومًا على continue داخل حلقة يُحدَّث عدّادها داخل الجسم بدلًا من الترويسة، فقد تتخطّى ذلك التحديث عن طريق الخطأ وتدور إلى الأبد.

الحلقات المتداخلة

ضع حلقة for داخل أخرى للعمل مع الشبكات أو الجداول أو الأزواج. تُنفَّذ الحلقة الداخلية بالكامل عند كل خطوة مفردة من الحلقة الخارجية.

تطبع هذه الشيفرة شبكة ضرب بحجم 3x3. تثبّت الحلقة الخارجية قيمة row، وتمسح الحلقة الداخلية كل col لذلك الصف، ثم يُنهي سطر جديد الصف. امنح العدّادات أسماء مميّزة (row/col، وليس i/i) - فإعادة استخدام الاسم نفسه تحجب الخارجي وتُنتج أخطاءً محيّرة. وانتبه للتكلفة أيضًا: تداخل حلقة بحجم n داخل حلقة بحجم n يُنفّذ الجسم n * n مرة، وهو ما يتراكم بسرعة.

مزالق شائعة

تفسّر بضعة فخاخ معظم أخطاء حلقات for في C++:

  • الانزياح بمقدار واحد: i <= n مع حجم يبدأ من الصفر يقرأ عنصرًا واحدًا بعد النهاية. استخدم i < n.
  • التدفّق السفلي غير الموقّع (underflow): العدّ التنازلي بنوع غير موقّع لا يصبح سالبًا أبدًا. تدور for (size_t i = n - 1; i >= 0; i--) إلى الأبد، لأن i >= 0 صحيح دائمًا لقيمة غير موقّعة - فعندما تكون i تساوي 0، يلتفّ i-- إلى عدد موجب ضخم. استخدم int موقّعًا للعدّ التنازلي، أو أعد كتابة الشرط.
  • تعديل العدّاد داخل الجسم: تغيير i داخل الجسم إضافةً إلى الترويسة يجعل عدد مرات الحلقة غير قابل للتنبّؤ. اختر مكانًا واحدًا.
// BUG: infinite loop - unsigned i is never < 0
for (size_t i = n - 1; i >= 0; i--) {
    process(arr[i]);
}

عدّادات الفاصلة العائمة خطر صامت آخر: قد لا تبلغ for (double x = 0.0; x != 1.0; x += 0.1) القيمة 1.0 بالضبط أبدًا لأن 0.1 لا يمكن تخزينها بدقّة. كرّر بعدّ صحيح واحسب القيمة في الداخل، أو استخدم < بدلًا من !=.

التالي: حلقات while

تتألّق حلقة for عندما تعرف العدد مسبقًا. لكنك أحيانًا تحتاج إلى التكرار حتى يتغيّر شرط ما - قراءة الإدخال حتى نهاية الملف، أو إعادة المحاولة حتى النجاح - دون عدد ثابت من الخطوات. تلك مهمّة حلقة while، التي تختزل الترويسة إلى شرط واحد فقط. وهي الصفحة التالية.

الأسئلة الشائعة

كيف تكتب حلقة for في C++؟

ضع ثلاثة أجزاء في الترويسة مفصولة بفواصل منقوطة: مُهيِّئ، وشرط، وتحديث. تشغّل for (int i = 0; i < 5; i++) { cout << i; } الجسم بينما تأخذ i القيم 0 و1 و2 و3 و4. تتوقف الحلقة بمجرد أن يصبح الشرط false.

ما الفرق بين حلقة for وحلقة for المبنية على نطاق في C++؟

تمنحك حلقة for الكلاسيكية عدّاد فهرس تتحكم به (for (int i = 0; i < n; i++))، وتحتاجه عندما تريد الموضع أو تريد التقدّم بخطوة مخصّصة. أما حلقة for المبنية على نطاق (for (int x : v)) فتخفي الفهرس وتسلّمك كل عنصر مباشرة - وهي أنظف عندما تحتاج القيم فقط.

لماذا تُنفَّذ حلقة for في C++ مرة زائدة أو مرة ناقصة؟

هذا هو خطأ الانزياح بمقدار واحد (off-by-one) الكلاسيكي. استخدام <= بدلًا من < مع حجم يبدأ من الصفر يُنفّذ تكرارًا إضافيًا ويقرأ ما بعد نهاية المصفوفة؛ واستخدام < بينما كنت تقصد تضمين القيمة الأخيرة يُنفّذ مرة ناقصة. لمصفوفة حجمها n، النمط الآمن هو for (int i = 0; i < n; i++).

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن