Vòng lặp trong C++: for, while, do while, break và continue

Vòng lặp trong C++

Trong bài này chúng ta sẽ học vòng lặp trong C++: for, while, do while, vòng lặp for duyệt mảng của C++11, cùng 2 lệnh điều khiển break và continue. Có vòng lặp, chương trình thư viện của chúng ta sẽ không còn chạy một lần rồi tắt, mà hiện menu liên tục cho tới khi người dùng chọn thoát.

Bài 7 trong Serie Lập trình C++ từ A tới Z

Vòng lặp là gì?

Vòng lặp (loop) cho phép chạy lại một khối lệnh nhiều lần, chừng nào điều kiện còn đúng. Thay vì viết 100 dòng std::cout để in 100 cuốn sách, chúng ta viết 1 dòng và cho nó lặp 100 lần.

Ví dụ đời thường: thủ thư kiểm kê kho sách. Lấy cuốn đầu tiên, ghi lại, lấy cuốn tiếp theo, ghi lại… cho tới khi hết kệ thì dừng. “Hết kệ chưa?” chính là điều kiện của vòng lặp.

C++ có 3 loại vòng lặp chính:

Vòng lặp Dùng khi
for Biết trước số lần lặp, ví dụ duyệt từ 1 tới n
while Chưa biết số lần lặp, lặp cho tới khi điều kiện sai
do while Giống while, nhưng thân vòng lặp luôn chạy ít nhất 1 lần

Vòng lặp for

Cú pháp vòng lặp for gồm 3 phần ngăn cách bởi dấu ;:

for (khoi_tao; dieu_kien; cap_nhat)
{
    // than vong lap
}

Ví dụ tính tổng từ 1 tới n:

#include <iostream>

int main()
{
    int n;
    std::cout << "Nhap n: ";
    std::cin >> n;

    int tong = 0;
    for (int i = 1; i <= n; i++)
    {
        tong += i;
    }
    std::cout << "1 + 2 + ... + " << n << " = " << tong << '\n';
    return 0;
}

Vòng lặp for tính tổng từ 1 tới 100 bằng 5050

Thứ tự chạy của vòng lặp for như sau:

Thứ tự chạy vòng lặp for: khởi tạo, kiểm tra điều kiện, chạy thân, cập nhật rồi quay lại

  1. Khởi tạo int i = 1 chỉ chạy đúng 1 lần, trước khi vào vòng lặp.
  2. Kiểm tra điều kiện i <= n. Đúng thì chạy thân vòng lặp, sai thì thoát.
  3. Chạy thân vòng lặp tong += i.
  4. Cập nhật i++, rồi quay lại bước 2.

Biến i khai báo trong for chỉ sống trong vòng lặp, ra ngoài là bị hủy. Như vậy các vòng lặp khác nhau đều đặt tên biến đếm là i được mà không đụng nhau.

Vòng lặp while

while chỉ có phần điều kiện, dùng khi chưa biết trước phải lặp bao nhiêu lần. Ví dụ bắt người dùng nhập lại cho tới khi số cuốn sách mượn hợp lệ:

#include <iostream>

int main()
{
    int soCuon;
    std::cout << "Muon bao nhieu cuon (1-5): ";
    std::cin >> soCuon;

    while (soCuon < 1 || soCuon > 5)
    {
        std::cout << "Khong hop le, nhap lai (1-5): ";
        std::cin >> soCuon;
    }
    std::cout << "Ban muon " << soCuon << " cuon\n";
    return 0;
}

Vòng lặp while bắt nhập lại số cuốn sách cho tới khi hợp lệ

Người dùng nhập sai bao nhiêu lần thì vòng lặp chạy bấy nhiêu lần, không đoán trước được. Đây là chỗ while hợp hơn for.

Lưu ý: Trong thân while bắt buộc phải có câu lệnh làm thay đổi điều kiện, ở đây là lệnh nhập lại soCuon. Quên dòng đó thì điều kiện mãi đúng, chương trình lặp vô hạn. Gặp trường hợp này, các bạn nhấn Ctrl + C trong terminal để dừng chương trình.

Vòng lặp do while

do while kiểm tra điều kiện ở cuối vòng lặp, nên thân vòng lặp luôn chạy ít nhất 1 lần. Rất hợp với menu: phải hiện menu ra trước thì người dùng mới chọn được.

Giờ chúng ta ghép do while với switch của bài trước để menu thư viện chạy liên tục:

#include <iostream>

int main()
{
    int luaChon;
    int soSachDangMuon = 0;

    do
    {
        std::cout << "\n1. Muon sach  2. Tra sach  0. Thoat\n";
        std::cout << "Chon: ";
        std::cin >> luaChon;

        switch (luaChon)
        {
        case 1:
            soSachDangMuon++;
            std::cout << "Da muon, dang giu " << soSachDangMuon << " cuon\n";
            break;
        case 2:
            if (soSachDangMuon > 0)
            {
                soSachDangMuon--;
                std::cout << "Da tra, dang giu " << soSachDangMuon << " cuon\n";
            }
            else
            {
                std::cout << "Ban khong giu cuon nao\n";
            }
            break;
        case 0:
            std::cout << "Tam biet!\n";
            break;
        default:
            std::cout << "Lua chon khong hop le\n";
            break;
        }
    } while (luaChon != 0);
    return 0;
}

Menu thư viện chạy lặp lại bằng do while kết hợp switch case cho tới khi chọn 0

Chú ý dấu ; sau while (luaChon != 0). Đây là chỗ duy nhất vòng lặp kết thúc bằng dấu chấm phẩy, quên nó là lỗi biên dịch.

Lệnh break bên trong switch chỉ thoát khỏi switch, không thoát khỏi vòng lặp do while. Vòng lặp chỉ dừng khi điều kiện luaChon != 0 sai.

Vòng lặp for duyệt mảng (range-based for)

Từ C++11 có thêm cách viết for rất gọn để duyệt qua từng phần tử của một danh sách:

#include <iostream>
#include <string>

int main()
{
    std::string danhSach[] = {"Lap trinh C", "Lap trinh C++", "Cau truc du lieu", "Python co ban"};

    int stt = 1;
    for (const std::string& ten : danhSach)
    {
        std::cout << stt << ". " << ten << '\n';
        stt++;
    }
    return 0;
}

Range-based for in danh sách tên sách trong mảng std::string

Đọc dòng for này là: “với mỗi ten trong danhSach“. Không cần biến đếm, không cần biết mảng có bao nhiêu phần tử, nên không bao giờ duyệt lố ra ngoài mảng.

Dấu & trong const std::string& giúp không phải sao chép từng chuỗi, còn const đảm bảo không sửa nhầm phần tử. Chi tiết về tham chiếu & chúng ta sẽ học ở bài về hàm, giờ các bạn cứ dùng theo mẫu này khi duyệt danh sách chỉ để đọc.

Lệnh break và continue

  • continue: bỏ qua phần còn lại của lần lặp hiện tại, nhảy sang lần lặp tiếp theo.
  • break: thoát hẳn khỏi vòng lặp.

Ví dụ danh sách số ngày mượn của 6 cuốn sách, 0 là sách đã trả:

#include <iostream>

int main()
{
    int soNgayMuon[] = {5, 12, 0, 20, 9, 30};   // 0 la sach da tra
    constexpr int HAN = 14;

    std::cout << "Sach dang muon chua qua han:\n";
    for (int i = 0; i < 6; i++)
    {
        if (soNgayMuon[i] == 0)
            continue;               // bo qua sach da tra
        if (soNgayMuon[i] > HAN)
            continue;               // bo qua sach qua han
        std::cout << "  Cuon " << i << ": " << soNgayMuon[i] << " ngay\n";
    }

    for (int i = 0; i < 6; i++)
    {
        if (soNgayMuon[i] > HAN)
        {
            std::cout << "Cuon dau tien qua han: cuon " << i << '\n';
            break;                  // tim thay roi thi dung
        }
    }
    return 0;
}

Dùng continue để bỏ qua sách đã trả, break để dừng khi tìm thấy sách quá hạn đầu tiên

Vòng lặp thứ nhất dùng continue để lọc bỏ những cuốn không cần in. Vòng lặp thứ hai dùng break: tìm thấy cuốn quá hạn đầu tiên (cuốn 3) là dừng, không cần xét cuốn 5 nữa.

Chỉ số mảng trong C++ bắt đầu từ 0, nên mảng 6 phần tử có chỉ số từ 0 tới 5. Vì vậy điều kiện là i < 6, không phải i <= 6. Phần mảng chúng ta sẽ học kỹ ở bài mảng và con trỏ.

Vòng lặp lồng nhau

Vòng lặp có thể nằm trong vòng lặp khác. Với mỗi lần lặp của vòng ngoài, vòng trong chạy trọn một lượt:

#include <iostream>

int main()
{
    for (int i = 2; i <= 4; i++)
    {
        for (int j = 1; j <= 5; j++)
        {
            std::cout << i << " x " << j << " = " << i * j << "\t";
        }
        std::cout << '\n';
    }
    return 0;
}

Hai vòng lặp for lồng nhau in bảng cửu chương 2, 3, 4

Vòng ngoài chạy 3 lần (i từ 2 tới 4), mỗi lần vòng trong chạy 5 lần, tổng cộng 15 phép nhân. Ký tự \t là phím tab, giúp các cột thẳng hàng.

Để ý số lần lặp nhân lên rất nhanh: 2 vòng lồng nhau, mỗi vòng 1000 lần là 1 triệu lần chạy. Lồng 3 vòng là 1 tỷ, chương trình sẽ chạy chậm thấy rõ.

Lỗi vòng lặp vô hạn với số unsigned

Đếm ngược từ 3 về 0 trông rất đơn giản:

#include <iostream>

int main()
{
    for (unsigned int i = 3; i >= 0; i--)
    {
        std::cout << "Con " << i << " giay\n";
    }
    std::cout << "Bat dau!\n";
    return 0;
}

Vòng lặp vô hạn khi đếm ngược bằng unsigned int, g++ cảnh báo comparison always true

Chương trình không bao giờ in “Bat dau!”. Biến unsigned không bao giờ âm, nên i >= 0 luôn đúng. Khi i bằng 0 mà trừ thêm 1, giá trị quay vòng thành 4294967295 như bài các kiểu dữ liệu trong C++ đã nói, và vòng lặp chạy mãi. Mình phải dùng | head -7 để chỉ lấy 7 dòng đầu, không thì terminal sẽ in không ngừng.

Lưu ý: Đếm ngược thì dùng biến int có dấu. g++ cảnh báo comparison of unsigned expression in '>= 0' is always true nhờ cờ -Wextra, bỏ cờ này đi là không có cảnh báo gì.

Bài tập

  1. In ra các số chẵn từ 1 tới 50 trên cùng một dòng, cách nhau bởi dấu cách.
  2. Nhập một số nguyên dương, in ra tổng các chữ số của nó. Ví dụ 2026 thì in ra 10. Gợi ý: dùng while với % 10 và / 10.
  3. Nhập điểm của nhiều sinh viên, nhập -1 để kết thúc. In ra điểm trung bình. Nên dùng loại vòng lặp nào?
  4. Thêm chức năng “3. Xem so sach dang muon” vào menu do while ở trên.

Lời giải sẽ có trong bài tổng hợp bài tập C++ cơ bản nhé.

Kết

Vòng lặp trong C++ có 3 loại chính, chọn đúng loại giúp code dễ đọc hơn hẳn: biết trước số lần lặp thì dùng for, chưa biết thì dùng while, cần chạy ít nhất 1 lần thì dùng do while, còn duyệt danh sách thì dùng range-based for. Lỗi đáng sợ nhất là vòng lặp vô hạn, nên mỗi khi viết vòng lặp hãy tự hỏi “điều kiện dừng ở đâu?”.

Các bạn đã học vòng lặp while và for trong C sẽ thấy cú pháp y hệt, C++ chỉ thêm range-based for. Bài trước là lệnh rẽ nhánh C++. Ở bài tiếp theo chúng ta sẽ tách chương trình thành nhiều hàm cho gọn, vì main của chương trình thư viện bắt đầu dài quá rồi. Nếu thấy bài viết có ích, hãy chia sẻ cho bạn bè cùng học nhé.

Rate this post

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *