שתי דרכים לשנות את מהלך הלולאה
התנאי של לולאה קובע מתי היא נגמרת, אבל לפעמים התשובה האמיתית מתגלה באמצע הגוף. אתם מחפשים במערך ומוצאים את הפריט באיבר השלישי: למה לבדוק את 997 האחרים? או שאתם מעבדים רשימה ורשומה אחת ריקה: מדלגים עליה וממשיכים.
ל-C יש מילת מפתח אחת לכל מקרה:
break: צאו מהלולאה עכשיו. הביצוע ממשיך בפקודה הראשונה שאחריה.continue: נטשו רק את הסיבוב הזה. המשיכו לסיבוב הבא.
הלולאה הראשונה מדפיסה 1 2 3 4 ועוצרת. השנייה מדפיסה 1 2 3 4 6 7 8 9 10: היא דילגה על ערך אחד והמשיכה.
break בלולאה
השימוש הקלאסי הוא חיפוש שעוצר ברגע שהוא מצליח:
מכיוון ש-found מקבל ערך לפני ה-break, הקוד שאחרי הלולאה יכול להבחין בין "מצאתי" לבין "נגמרו האיברים". הצמד הזה, משתנה תוצאה ועוד break, הוא ניב החיפוש המקובל ב-C. אי אפשר להשתמש במונה הלולאה עצמו אחר כך כשהוא מוצהר בכותרת, כי הוא יוצא מהטווח בסוגר המסולסל הסוגר.
break תמיד יוצאת מלולאה אחת: ה-for, while, do-while או switch הקרובים ביותר שעוטפים אותה.
continue בלולאה
continue מיועדת לרשומות שצריך לדלג עליהן במקום לעצור את העבודה:
לאן continue קופצת תלוי בסוג הלולאה:
for -> the update part of the header (so the counter still advances)
while -> the condition test (nothing else in the body runs)
do-while -> the condition test at the bottom
ההבדל הזה אינו תיאורטי. בלולאת for, continue לעולם לא יכולה לדלג על הגדלת המונה, כי ההגדלה נמצאת בכותרת. בלולאת while היא יכולה:
/* BUG: continue skips i++, so this hangs at the first even number */
int i = 0;
while (i < 10) {
if (i % 2 == 0) {
continue;
}
printf("%d ", i);
i++;
}
או שתעבירו את i++ מעל ה-continue, או שתכתבו את זה כלולאת for, שבה הכותרת הופכת את הטעות לבלתי אפשרית.
break בתוך switch אינה break בלולאה
break מסיימת את הלולאה או את ה-switch הקרובים ביותר שעוטפים אותה, מה שקרוב יותר. שימו switch בתוך לולאה, וכל break בתוכו שייכת ל-switch:
הפקודה q מדפיסה את ההודעה שלה והלולאה ממשיכה לרוץ עד cc. אם היציאה הייתה אמורה לסיים את הלולאה, ה-break הזו לא עשתה את זה. הפתרונות הם דגל, goto, או העברת הלולאה לפונקציה ושימוש ב-return, וכולם מוצגים בהמשך.
יציאה מלולאות מקוננות
break יחידה יוצאת מרמה אחת. בתוך שתי לולאות מקוננות היא מסיימת רק את הפנימית, והלולאה החיצונית מתחילה את הסיבוב הבא שלה כאילו לא קרה כלום.
ל-C אין break 2 ואין break עם תווית. שלוש אפשרויות כנות:
דגל. נייד, ברור וקצת רועש:
הדגל מופיע בשני מקומות, בתנאי החיצוני ובהשמה, וזה המחיר. עם שלוש רמות קינון זה נהיה ממש לא נעים.
goto. הקפיצה של C שכולם אוהבים לשנוא, וזה המקרה היחיד שבו מתכנתי C מנוסים משתמשים בה בלי להתנצל:
goto קדימה לתווית שנמצאת מיד אחרי הלולאות עושה בדיוק מה ש-break עם תווית היה עושה בשפות אחרות, בשורה אחת ובלי דגל שצריך לשמור מסונכרן. המוניטין של goto נבנה בזכות קפיצות אחורה וקפיצות לאמצע של בלוקים אחרים, שבאמת מייצרות זרימת בקרה בלתי קריאה. קפיצה אחת קדימה מתוך לולאות מקוננות אינה כזו. ליבת Linux משתמשת באותה תבנית עבור מסלולי ניקוי. השתמשו בה בכוונה ובמשורה; אל תתנו לטאבו לדחוף אתכם לחלופה עם שלושה דגלים שקשה יותר לקרוא.
פונקציה ו-return. בדרך כלל התשובה הטובה ביותר, כי חיפוש ראוי לשם:
return יוצאת מכל הלולאות בפונקציה, לא צריכה דגל ולא תווית, ולקוד החיפוש יש עכשיו שם ואפשר לבדוק אותו בנפרד. כשהקינון עמוק מספיק כדי שהיציאה ממנו תהיה בעיה, זה לרוב רמז שהבלוק רצה להיות פונקציה בכל מקרה.
מתי הן עוזרות ומתי הן מזיקות
break ו-continue מצדיקות את מקומן כשהן מאפשרות ללולאה לומר "המקרה הזה הסתיים" פעם אחת, קרוב לראש הגוף, במקום לעטוף את כל הגוף ב-if. השוו:
/* with continue: the real work is not indented */
for (int i = 0; i < n; i++) {
if (!isValid(a[i])) continue;
if (isDuplicate(a[i])) continue;
process(a[i]);
}
/* without: every guard adds a level */
for (int i = 0; i < n; i++) {
if (isValid(a[i])) {
if (!isDuplicate(a[i])) {
process(a[i]);
}
}
}
הן מזיקות כשיש הרבה מהן. ללולאה עם חמש פקודות break מפוזרות ושלוש continue יש תנאי יציאה שלא כתוב בשום מקום: הקורא צריך לדמות את כל הגוף כדי לדעת מתי היא עוצרת. שני כללים שמחזיקים מעמד היטב: שמרו את שומרי ה-continue יחד בראש הגוף, ואם ללולאה יש יותר מ-break אחת או שתיים, שקלו אם התנאי בכותרת היה צריך לעשות את העבודה הזו.
שתי מלכודות קטנות יותר. continue בתוך do-while קופצת לבדיקה שבתחתית, שהיא ממילא הדבר הבא: זה לא מזיק, אבל זה מפתיע אנשים. ו-break מחוץ לכל לולאה או switch היא שגיאת קומפילציה, וזו הטעות היחידה כאן שהקומפיילר תופס בשבילכם.
שאלות נפוצות
מה ההבדל בין break ל-continue ב-C?
break מסיימת את הלולאה לגמרי, והביצוע ממשיך אחריה. continue נוטשת רק את הסיבוב הנוכחי וממשיכה לסיבוב הבא: לעדכון בלולאת for, או ישר לבדיקת התנאי ב-while או ב-do-while.
איך יוצאים מלולאה מקוננת ב-C?
break יחידה יוצאת רק מהלולאה הפנימית ביותר. כדי לצאת משתיהן, אפשר להציב דגל ולבדוק אותו בתנאי של הלולאה החיצונית, להשתמש ב-goto כדי לקפוץ לתווית שאחרי הלולאות, או, בדרך כלל הפתרון הנקי ביותר, להעביר את הלולאות לפונקציה ולעשות return.
האם break יוצאת מהלולאה או רק מה-switch?
ממה שעוטף אותה הכי קרוב. break בתוך switch שנמצא בתוך לולאה מסיימת רק את ה-switch; הלולאה ממשיכה. כדי לצאת מהלולאה מתוך switch צריך דגל, goto או return.
למה continue גורמת ללולאה אינסופית בלולאת ה-while שלי?
כי continue קופצת לתנאי ומדלגת על שאר גוף הלולאה, כולל הגדלת המונה אם היא נמצאת בסוף. בלולאת for העדכון נמצא בכותרת ולכן תמיד רץ; בלולאת while, העבירו את העדכון לפני ה-continue או השתמשו ב-for.