Menu

לולאת for ב-C++: תחביר, דוגמאות וטעויות נפוצות

איך חוזרים על קוד עם לולאת for ב-C++: הכותרת בת שלושת החלקים, ספירה למעלה ולמטה, מעבר על מערכים, קינון, break ו-continue, והבאגים של off-by-one ו-unsigned שפוגעים בכולם.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

למה לולאת 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] היא undefined behavior: היא עלולה להדפיס זבל, לקרוס, או להיראות כאילו היא עובדת ולהשחית זיכרון בשקט. הדפוס 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++:

  • Off-by-one: i <= n עם גודל שמתחיל מאפס קורא איבר אחד אחרי הסוף. השתמשו ב-i < n.
  • גלישה של unsigned כלפי מטה: ספירה לאחור עם טיפוס unsigned אף פעם לא הופכת לשלילית. for (size_t i = n - 1; i >= 0; i--) רצה לנצח, כי i >= 0 תמיד נכון עבור ערך unsigned: כש-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

ללמוד תכנות עם Coddy

להתחיל