Menu

Break và continue trong C: cách hoạt động và khi nào dùng

break thoát khỏi vòng lặp hoặc switch; continue bỏ qua phần còn lại để sang lượt kế tiếp. Cách mỗi lệnh hoạt động trong for, while và do-while, cách thoát vòng lặp lồng nhau bằng biến cờ hoặc goto, và những cách dùng sai khiến vòng lặp khó theo dõi.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Hai cách thay đổi luồng chạy của vòng lặp

Điều kiện của vòng lặp quyết định khi nào nó kết thúc, nhưng đôi khi câu trả lời thật sự lại xuất hiện ở giữa thân vòng lặp. Bạn đang tìm trong một mảng và thấy phần tử cần tìm ở vị trí thứ ba, vậy kiểm tra 997 phần tử còn lại để làm gì? Hoặc bạn đang xử lý một danh sách và có một mục bị trống: bỏ qua nó rồi làm tiếp.

C có một từ khóa cho mỗi trường hợp:

  • break: thoát khỏi vòng lặp ngay. Chương trình chạy tiếp từ câu lệnh đầu tiên sau vòng lặp.
  • continue: chỉ bỏ lượt này. Chuyển sang lượt kế tiếp.

Vòng lặp đầu in ra 1 2 3 4 rồi dừng. Vòng lặp thứ hai in ra 1 2 3 4 6 7 8 9 10: nó bỏ qua một giá trị và chạy tiếp.

break trong vòng lặp

Cách dùng điển hình là một phép tìm kiếm dừng ngay khi tìm thấy:

Vì found được gán trước break, đoạn code sau vòng lặp phân biệt được giữa "đã tìm thấy" và "đã hết phần tử". Cặp đôi này, một biến lưu kết quả cộng với một break, là cách viết tìm kiếm chuẩn trong C. Bản thân biến đếm thì không dùng được sau vòng lặp nếu nó được khai báo ở phần đầu vòng lặp, vì nó ra khỏi phạm vi tại dấu ngoặc nhọn đóng.

break luôn chỉ thoát một vòng lặp: khối for, while, do-while hoặc switch bao nó gần nhất.

continue trong vòng lặp

continue dành cho những mục cần bỏ qua chứ không phải dừng cả công việc:

continue nhảy tới đâu tùy vào loại vòng lặp:

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

Khác biệt đó không chỉ là lý thuyết. Trong vòng lặp for, continue không bao giờ bỏ qua lệnh tăng biến đếm, vì lệnh tăng nằm ở phần đầu vòng lặp. Trong vòng lặp while thì có thể:

/* 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++;
}

Hoặc đưa i++ lên trên continue, hoặc viết lại thành vòng lặp for, nơi phần đầu vòng lặp khiến bạn không thể viết sai.

break trong switch không phải break của vòng lặp

break kết thúc vòng lặp hoặc switch bao nó gần nhất, cái nào gần hơn thì tính cái đó. Đặt một switch bên trong vòng lặp thì mọi break trong đó đều thuộc về switch:

Lệnh q in ra thông báo và vòng lặp vẫn chạy tiếp qua cc. Nếu thoát được dự định để kết thúc vòng lặp thì break đó đã không làm được. Cách sửa là dùng biến cờ, dùng goto, hoặc đưa vòng lặp vào một hàm và dùng return, tất cả được trình bày bên dưới.

Thoát khỏi vòng lặp lồng nhau

Một break chỉ thoát một cấp. Bên trong hai vòng lặp lồng nhau, nó chỉ kết thúc vòng lặp trong, còn vòng lặp ngoài bắt đầu lượt kế tiếp như chưa có gì xảy ra.

C không có break 2 và cũng không có break kèm nhãn. Có ba lựa chọn đàng hoàng:

Biến cờ. Chạy được ở mọi nơi, dễ hiểu, hơi rườm rà một chút:

Biến cờ xuất hiện ở hai chỗ, trong điều kiện của vòng lặp ngoài và ở lệnh gán, đó là cái giá phải trả. Với ba cấp lồng nhau thì nó thật sự khó chịu.

goto. Lệnh nhảy bị chê trách nhiều nhất của C, và đây là trường hợp duy nhất các lập trình viên C giàu kinh nghiệm dùng nó mà không cần biện minh:

Một goto nhảy tới trước, tới một nhãn ngay sau các vòng lặp, làm đúng việc mà break kèm nhãn làm trong các ngôn ngữ khác, chỉ trong một dòng và không cần biến cờ nào phải giữ đồng bộ. Tiếng xấu của goto đến từ các cú nhảy ngược lại và các cú nhảy vào giữa những khối lệnh khác, những thứ thật sự tạo ra luồng điều khiển khó đọc. Một cú nhảy tới trước để thoát khỏi vòng lặp lồng nhau không thuộc loại đó. Nhân Linux dùng đúng mẫu này cho các đường dọn dẹp tài nguyên. Hãy dùng nó có chủ đích và tiết chế; đừng để điều cấm kỵ đẩy bạn tới một giải pháp ba biến cờ còn khó đọc hơn.

Một hàm và return. Thường là cách tốt nhất, vì một phép tìm kiếm xứng đáng có một cái tên:

return thoát khỏi mọi vòng lặp trong hàm, không cần biến cờ hay nhãn, và đoạn code tìm kiếm giờ có tên riêng và có thể được kiểm thử độc lập. Khi các vòng lặp lồng nhau sâu đến mức việc thoát ra trở thành vấn đề, đó thường là dấu hiệu khối code ấy vốn nên là một hàm.

Khi nào chúng có ích, khi nào gây hại

break và continue đáng dùng khi chúng cho phép vòng lặp nói "trường hợp này xong rồi" một lần, ở gần đầu thân vòng lặp, thay vì bọc cả thân trong một if. So sánh:

/* 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]);
        }
    }
}

Chúng gây hại khi có quá nhiều. Một vòng lặp với năm break và ba continue rải rác có một điều kiện thoát không được viết ra ở đâu cả: người đọc phải tự mô phỏng toàn bộ thân vòng lặp mới biết khi nào nó dừng. Hai nguyên tắc rất đáng tin: gom các lệnh continue dùng để chặn lại với nhau ở đầu thân vòng lặp, và nếu một vòng lặp có nhiều hơn một hoặc hai break, hãy xem liệu điều kiện ở phần đầu vòng lặp có nên đảm nhận việc đó không.

Thêm hai cái bẫy nhỏ. continue trong do-while nhảy tới phép kiểm tra ở cuối, vốn cũng là bước tiếp theo: vô hại, nhưng khiến nhiều người bất ngờ. Còn break nằm ngoài mọi vòng lặp hay switch là lỗi biên dịch, sai lầm duy nhất ở đây mà trình biên dịch bắt giúp bạn.

Câu hỏi thường gặp

Khác nhau giữa break và continue trong C là gì?

break kết thúc hẳn vòng lặp và chương trình chạy tiếp từ sau vòng lặp. continue chỉ bỏ lượt hiện tại và chuyển sang lượt kế tiếp: tới phần cập nhật trong vòng lặp for, hoặc thẳng tới phép kiểm tra điều kiện trong while hay do-while.

Làm sao thoát khỏi vòng lặp lồng nhau trong C?

Một lệnh break chỉ thoát khỏi vòng lặp trong cùng. Muốn thoát cả hai, bạn có thể đặt một biến cờ và kiểm tra nó trong điều kiện của vòng lặp ngoài, dùng goto nhảy tới một nhãn sau các vòng lặp, hoặc cách thường gọn nhất là đưa các vòng lặp vào một hàm rồi return.

break thoát khỏi vòng lặp hay chỉ thoát khỏi switch?

Thoát khỏi khối bao nó gần nhất. Một break trong switch nằm bên trong vòng lặp chỉ kết thúc switch; vòng lặp vẫn chạy tiếp. Muốn thoát vòng lặp từ bên trong switch, bạn cần một biến cờ, một goto hoặc một return.

Vì sao continue khiến vòng lặp while chạy vô hạn?

Vì continue nhảy tới điều kiện và bỏ qua phần còn lại của thân vòng lặp, kể cả lệnh tăng biến đếm nếu nó nằm ở cuối. Trong vòng lặp for, phần cập nhật nằm ở phần đầu nên luôn được chạy; trong vòng lặp while, hãy đưa phần cập nhật lên trước continue hoặc chuyển sang dùng for.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu