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;
}
Thứ tự chạy của vòng lặp for như sau:
- Khởi tạo
int i = 1chỉ chạy đúng 1 lần, trước khi vào vòng lặp. - Kiểm tra điều kiện
i <= n. Đúng thì chạy thân vòng lặp, sai thì thoát. - Chạy thân vòng lặp
tong += i. - 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;
}
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;
}
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;
}
Đọ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;
}
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;
}
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;
}
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
- 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.
- 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
whilevới% 10và/ 10. - 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?
- 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é.









