Vòng lặp trong C
Có lúc ta muốn lặp đi lặp lại một việc nhiều lần: cộng dồn 100 số, in bảng cửu chương,
hỏi lại người dùng cho tới khi nhập đúng. Vòng lặp (loop) cho phép chạy một khối
lệnh nhiều lần mà không phải viết lại. C có ba vòng lặp: for, while và do-while.
Hãy hình dung bạn phải tính tổng điểm của 50 sinh viên. Không có vòng lặp, bạn buộc phải viết 50 dòng cộng gần như giống hệt nhau, và nếu lớp tăng lên 51 sinh viên thì phải mở mã nguồn ra sửa. Vòng lặp cắt đứt sự phụ thuộc đó: bạn viết một lần cái việc cần làm, rồi bảo máy hãy làm lại nó chừng nào điều kiện còn đúng. Số lần lặp trở thành một dữ liệu, không còn là phần cứng nhắc của mã nguồn.
Mọi vòng lặp, dù viết bằng cú pháp nào, đều lắp từ ba bộ phận. Thứ nhất là điểm bắt đầu, tức trạng thái trước khi lặp. Thứ hai là điều kiện tiếp tục, câu hỏi máy đặt ra trước mỗi vòng để quyết định chạy tiếp hay dừng. Thứ ba là bước tiến, thay đổi làm trạng thái nhích dần về phía điều kiện trở nên sai. Thiếu bộ phận thứ ba thì vòng lặp không bao giờ dừng, và đó là lỗi phổ biến nhất của người mới. Ba vòng lặp của C chỉ khác nhau ở chỗ đặt ba bộ phận ấy ở đâu và kiểm tra điều kiện vào lúc nào.
Thử ngay: xem vòng for chạy từng bước
Bạn chưa cần thuộc cú pháp, cứ bấm ▶ Chạy (hoặc kéo thanh trượt) và nhìn hiện tượng
trước. Đoạn mã dưới đây là một vòng for tính tổng các số từ 1 tới 4: biến i thay đổi
qua từng vòng còn sum cộng dồn dần. Hãy chú ý lúc i thành 5, điều kiện sai nên vòng
lặp dừng. Ba bộ phận vừa kể nằm ở đâu trong dòng for ấy, các mục bên dưới sẽ mổ xẻ
từng phần.
1#include <stdio.h>2int main(void) {3 int n = 4, sum = 0;4 for (int i = 1; i <= n; i++) {5 sum += i;6 }7 printf("Sum 1..%d = %d\n", n, sum);8 return 0;9}
Mỗi lần quay lại dòng for, hãy để ý hai việc xảy ra liên tiếp: cập nhật i++ rồi mới
kiểm tra i <= n. Vòng lặp chỉ dừng khi điều kiện sai, lúc i đã là 5.
Vòng lặp for
for thích hợp khi đã biết trước số lần lặp. Cú pháp gồm ba phần đặt trong cặp ngoặc,
cách nhau bởi dấu chấm phẩy: for (khoi_tao; dieu_kien; cap_nhat) { than }.
Phần khởi tạo chạy đúng một lần lúc đầu. Trước mỗi vòng, máy kiểm tra điều kiện: còn đúng thì chạy thân, rồi chạy phần cập nhật, rồi quay lại kiểm tra; khi điều kiện sai thì thoát.
#include <stdio.h>
int main(void) {
int n = 4, sum = 0;
for (int i = 1; i <= n; i++) { // i goes 1, 2, ..., n
sum += i; // accumulate the running total
}
printf("Sum 1..%d = %d\n", n, sum);
return 0;
}
Biến i gọi là biến đếm (counter). Nó bắt đầu từ 1, tăng thêm 1 sau mỗi vòng nhờ
i++, và vòng lặp dừng khi i vượt quá n. Biến sum cộng dồn từng giá trị; mẫu khởi
tạo bằng 0 rồi cộng dần này rất hay gặp khi tính tổng.
Thứ tự thực thi của for là chỗ nhiều bạn nhớ mang máng, nên ta viết hẳn ra. Máy chạy
phần khởi tạo đúng một lần. Rồi nó kiểm tra điều kiện; nếu sai thì thoát ngay, thân
không chạy lần nào. Nếu đúng thì chạy trọn thân, sau đó mới chạy phần cập nhật, rồi quay
lại kiểm tra điều kiện. Chú ý hai điều dễ nhầm: phần cập nhật chạy sau thân chứ không
phải trước, và điều kiện được kiểm tra thêm một lần cuối cùng để phát hiện mình đã sai
thì mới thoát. Vì lần kiểm tra cuối này, sau khi vòng lặp kết thúc, biến i luôn mang
giá trị vượt qua ngưỡng chứ không phải bằng ngưỡng. Với for (int i = 1; i <= 4; i++)
thì lúc thoát i bằng 5, không phải 4.
Biến đếm khai bên trong ngoặc for chỉ sống trong lòng vòng lặp, ra khỏi vòng là nó biến
mất; nếu cần biết i bằng bao nhiêu lúc thoát thì phải khai i bên ngoài. Phần cập nhật
cũng không bắt buộc là i++: bạn viết được i += 2 để nhảy hai đơn vị, i-- để đếm lùi,
hay i *= 2 để đi theo cấp số nhân. Còn i++ hay ++i ở đây hoàn toàn tương đương, vì
giá trị của biểu thức không được dùng tới.
Vòng lặp while
while kiểm tra điều kiện trước mỗi vòng; còn đúng thì chạy thân. Nó thích hợp khi chưa
biết trước số lần lặp mà chỉ biết điều kiện dừng.
#include <stdio.h>
int main(void) {
int n = 0;
int count = 0;
n = 285;
while (n > 0) { // keep going until n becomes 0
n = n / 10; // remove the last digit each round
count++; // count one more digit
}
printf("Number of digits: %d\n", count);
return 0;
}
Mỗi vòng, phép n / 10 bỏ đi chữ số cuối (vì là chia nguyên), và count tăng 1. Khi n
về 0 thì hết chữ số, điều kiện n > 0 sai, vòng lặp dừng. Vì biến điều khiển n thay đổi
ngay trong thân, ta không cần phần cập nhật riêng như for.
Trong mọi vòng lặp, thân phải làm điều kiện dần tiến tới sai, nếu không sẽ lặp vô hạn
(infinite loop). Ví dụ nếu quên dòng n = n / 10, điều kiện n > 0 luôn đúng và chương
trình treo.
Vòng lặp do-while
do-while chạy thân trước, kiểm tra điều kiện sau, nên thân luôn chạy ít nhất một lần.
Nó hợp với việc cần làm một lần rồi mới quyết định có lặp lại không, điển hình là vòng nhập
liệu có kiểm tra.
#include <stdio.h>
int main(void) {
int age;
do {
printf("Enter age (1..120): ");
scanf("%d", &age);
} while (age < 1 || age > 120); // repeat while the value is invalid
printf("Accepted age: %d\n", age);
return 0;
}
Vì phải hỏi tuổi ít nhất một lần rồi mới biết hợp lệ hay chưa, do-while phù hợp hơn
while. Chú ý dấu chấm phẩy bắt buộc sau while (...) ở cuối.
Hãy để ý một chi tiết dễ bỏ qua: điều kiện của do-while ở đây là điều kiện sai, tức
age < 1 || age > 120. Điều này ngược với trực giác, vì ta quen nghĩ vòng lặp chạy khi
điều kiện đúng. Nhưng đúng là như vậy: ta lặp lại việc hỏi chừng nào giá trị còn chưa
hợp lệ. Người mới hay viết nhầm thành while (age >= 1 && age <= 120), kết quả là
chương trình cứ hỏi mãi khi người dùng nhập đúng và thoát ngay khi người dùng nhập sai,
đúng ngược lại ý muốn. Mẹo tránh nhầm: đọc thành lời câu "lặp lại chừng nào còn ..." rồi
mới dịch sang mã.
Nếu viết cùng chức năng đó bằng while, bạn buộc phải lặp lại đoạn hỏi tuổi hai lần, một
lần trước vòng lặp để có giá trị đầu tiên mà kiểm tra, một lần trong thân. Đó chính là lý
do tồn tại của do-while: nó không mạnh hơn while, nó chỉ giúp bạn khỏi viết trùng.
Lệnh break và continue
Hai lệnh này điều khiển tinh hơn bên trong vòng lặp. break thoát ngay khỏi vòng lặp
đang chạy, bỏ qua phần còn lại của thân và mọi vòng kế tiếp. continue bỏ qua phần còn
lại của vòng hiện tại và nhảy sang vòng kế tiếp.
#include <stdio.h>
int main(void) {
int data[] = {3, -1, 5, 0, 9};
int sum = 0;
for (int k = 0; k < 5; k++) {
int x = data[k];
if (x == 0) {
break; // 0 marks the end, leave the loop
}
if (x < 0) {
continue; // skip negatives, do not add them
}
sum += x; // only positive numbers reach here
}
printf("Sum of positives = %d\n", sum);
return 0;
}
continue loại các số âm: gặp số âm thì bỏ qua ngay, không cộng. break dừng hẳn khi gặp
số 0. Chỉ số dương mới xuống tới dòng sum += x.
Hãy chạy thử bằng đầu. Mảng là {3, -1, 5, 0, 9}. Vòng k = 0, x bằng 3, không phải
0, không âm, nên sum thành 3. Vòng k = 1, x bằng -1, gặp continue nên máy nhảy
thẳng lên phần cập nhật k++, bỏ qua dòng cộng. Vòng k = 2, x bằng 5, sum thành 8.
Vòng k = 3, x bằng 0, gặp break nên máy thoát hẳn khỏi vòng lặp. Số 9 ở cuối mảng
không bao giờ được xét tới. Kết quả in ra là 8. Nếu bạn tưởng kết quả là 17 thì bạn đã
quên rằng break không chỉ bỏ qua vòng này, nó kết thúc luôn cả vòng lặp.
Có một khác biệt tinh vi phải nắm: continue nhảy tới phần cập nhật của vòng lặp, chứ
không nhảy về đầu thân. Trong for, phần cập nhật k++ vẫn chạy nên vòng lặp vẫn tiến.
Nhưng trong while, phần cập nhật nằm ngay trong thân, và continue có thể nhảy qua nó.
Đây là cái bẫy chết người. Ngoài ra đừng lạm dụng hai lệnh này: một vòng lặp rải rác năm
sáu chỗ break rất khó theo dõi. Dùng chúng khi chúng làm mã sáng ra, điển hình là thoát
sớm khi đã tìm thấy thứ cần tìm.
Trong vòng while, biến điều khiển thường được cập nhật ở cuối thân. Nếu bạn đặt một
lệnh continue phía trên dòng cập nhật đó, thì mỗi lần continue chạy, dòng cập nhật bị
nhảy qua, biến điều khiển đứng yên, điều kiện không bao giờ đổi, và chương trình treo
vĩnh viễn. Ví dụ đoạn while (i < n) { if (a[i] < 0) continue; sum += a[i]; i++; } sẽ
treo ngay khi gặp số âm đầu tiên. Cách chữa: hoặc đưa lệnh cập nhật lên trước mọi
continue, hoặc dùng for để phần cập nhật luôn được bảo đảm chạy.
Vòng lặp lồng nhau
Đặt một vòng lặp bên trong thân một vòng lặp khác gọi là vòng lặp lồng nhau (nested loop). Mỗi lần vòng ngoài chạy một bước, vòng trong chạy trọn cả chu trình của nó. Đây là công cụ chuẩn để xử lý bảng, ma trận hay mọi dữ liệu hai chiều.
#include <stdio.h>
int main(void) {
for (int i = 1; i <= 9; i++) { // outer loop: one row per i
for (int j = 1; j <= 9; j++) { // inner loop: columns 1..9
printf("%3d", i * j); // width 3 keeps columns aligned
}
printf("\n"); // newline after each row
}
return 0;
}
Vòng ngoài chọn dòng (giá trị i). Với mỗi i, vòng trong chạy hết j từ 1 tới 9 để in
9 tích trên cùng một dòng. Tổng cộng thân vòng trong chạy 81 lần, bằng tích số vòng ngoài
với số vòng trong.
Con số 81 ấy đáng suy nghĩ. Chi phí của vòng lồng nhau là phép nhân, không phải phép
cộng. Hai vòng lồng nhau, mỗi vòng chạy n lần, cho n nhân n lần chạy thân: với n
bằng 10 thì là 100 lần, chẳng đáng gì, nhưng với n bằng 10000 thì là 100 triệu lần, đủ
để chương trình chạy vài giây. Ba tầng lồng nhau thành n mũ 3, và n bằng 1000 đã là
một tỉ lần chạy. Mỗi khi định lồng thêm một tầng, hãy tự hỏi dữ liệu lớn cỡ nào. Ngoài
ra, mỗi biến đếm phải có tên riêng; quy ước phổ biến là i cho vòng ngoài, j cho vòng
trong, k cho tầng thứ ba.
Lỗi lệch một đơn vị
Có một loại lỗi xuất hiện nhiều tới mức người ta đặt hẳn tên riêng cho nó: lỗi lệch một đơn vị (off-by-one error). Vòng lặp của bạn chạy thừa đúng một vòng, hoặc thiếu đúng một vòng. Chương trình không sập, kết quả chỉ hơi sai, nên lỗi này rất khó phát hiện bằng mắt.
Gốc rễ nằm ở chỗ ta phải chọn giữa < và <=, và giữa việc bắt đầu từ 0 hay từ 1. Hãy
xem mảng a có 5 phần tử. Chỉ số hợp lệ là 0, 1, 2, 3, 4. Muốn duyệt hết, ta viết
for (int i = 0; i < 5; i++). Nếu lỡ viết i <= 5, vòng lặp chạy 6 lần và lần cuối cùng
chạm vào a[5], một ô nằm ngoài mảng. C không chặn, chương trình vẫn chạy, nhưng bạn
đang đọc rác hoặc phá bộ nhớ của biến khác.
Ngược lại, khi đếm từ 1 tới n như trong bài tính tổng, ta phải viết i <= n. Nếu lỡ
viết i < n, vòng lặp bỏ sót đúng số cuối cùng, và tổng thiếu mất một số hạng. Tổng
1 tới 4 sẽ ra 6 thay vì 10, sai mà nhìn qua vẫn tưởng hợp lý.
Cách tự vệ chắc nhất là thuộc lòng hai mẫu và không sáng tạo thêm. Duyệt mảng n phần tử
theo chỉ số thì luôn dùng for (int i = 0; i < n; i++). Đếm từ 1 tới n như trong toán
thì luôn dùng for (int i = 1; i <= n; i++). Cả hai mẫu đều chạy đúng n lần.
Thứ nhất, tổng hoặc số đếm luôn nhỏ hơn hoặc lớn hơn đáp án đúng đúng một đơn vị, chứ
không sai lung tung. Thứ hai, chương trình chạy đúng ở giữa nhưng sai ở phần tử đầu hoặc
phần tử cuối, hãy kiểm tra riêng hai đầu mút. Thứ ba, chương trình thỉnh thoảng cho kết
quả kỳ quặc hoặc sập, dấu hiệu bạn đã chạm ra ngoài mảng. Khi nghi ngờ, hãy chạy tay vòng
lặp với n bằng 1 hoặc n bằng 0, vì hầu hết lỗi lệch một đơn vị lộ ra ở các trường hợp
nhỏ nhất này.
Vòng lặp vô hạn
Vòng lặp vô hạn (infinite loop) là vòng lặp mà điều kiện không bao giờ trở thành sai. Chương trình không sập, nó chỉ chạy mãi, treo cứng, và bạn phải tự tay tắt nó đi.
Nguyên nhân gốc luôn là một trong ba lỗi sau. Một, bạn quên hẳn bước tiến; ví dụ viết
while (n > 0) { count++; } mà quên dòng n = n / 10, nên n không bao giờ giảm. Hai,
bước tiến có đó nhưng đi sai hướng; ví dụ for (int i = 10; i > 0; i++), biến i càng
tăng thì càng xa điều kiện dừng, không bao giờ nhỏ hơn hoặc bằng 0. Ba, điều kiện dừng
không bao giờ chạm được vì kiểu dữ liệu; ví dụ so sánh bằng nhau trên số thực như
while (x != 0.0) x -= 0.1;, vì sai số làm tròn khiến x có thể nhảy qua 0 mà không
bao giờ bằng đúng 0.
Không phải mọi vòng lặp vô hạn đều là lỗi. Đôi khi ta cố ý viết while (1) hoặc
for (;;) cho những chương trình phải chạy liên tục, chẳng hạn vòng lặp chính của một
trò chơi hay một máy chủ. Điểm khác biệt là trong trường hợp cố ý, bao giờ cũng có một
lệnh break bên trong thân, và bạn phải bảo đảm đường thoát ấy thật sự có thể xảy ra.
Trước khi bấm chạy một vòng lặp mới viết, hãy chỉ ngón tay vào dòng lệnh làm điều kiện
tiến gần tới chỗ sai. Nếu bạn không chỉ ra được dòng ấy, hoặc dòng ấy nằm sau một lệnh
continue, hoặc nó nằm trong một nhánh if có thể không chạy, thì vòng lặp của bạn có
nguy cơ treo. Thói quen nhỏ này tiết kiệm rất nhiều lần phải tắt chương trình bằng tay.
Chọn vòng lặp nào
Ba vòng lặp có cùng sức mạnh, bạn viết được bài nào bằng for thì cũng viết được bằng
while. Vì vậy việc chọn không phải chuyện đúng sai mà là chuyện diễn đạt cho người đọc
hiểu ngay ý bạn.
| tình huống | nên chọn | vì sao |
|---|---|---|
| biết trước số lần lặp | for | ba phần điều khiển gom gọn một chỗ, đọc phát biết ngay chạy bao nhiêu vòng |
| chỉ biết điều kiện dừng | while | không có biến đếm tự nhiên, số vòng phụ thuộc dữ liệu |
| phải chạy ít nhất một lần | do-while | kiểm tra điều kiện ở cuối, khỏi lặp mã trước vòng |
| lặp mãi tới khi có lệnh thoát | while (1) kèm break | ý đồ lặp vô hạn được nói thẳng ra |
Chọn đúng vòng lặp không làm chương trình chạy nhanh hơn, nhưng làm người sửa mã sau này, thường chính là bạn của vài tháng sau, đỡ mất thời gian đoán ý.
Bài tập thực hành
Bài tập 1: giai thừa bằng for
Dùng vòng lặp for tính và in giai thừa của một số nguyên dương n nhập từ bàn phím, với
n! = 1*2*...*n.
Lời giải chi tiết
Giai thừa là phép nhân dồn nên biến tích phải khởi tạo bằng 1 (không phải 0). Biến đếm i
chạy từ 1 tới n, mỗi vòng nhân thêm i vào kết quả.
#include <stdio.h>
int main(void) {
int n;
long long fact = 1; // start at 1 because we multiply
printf("Enter n: ");
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
fact *= i; // multiply the running product
}
printf("%d! = %lld\n", n, fact);
return 0;
}
Ý chính: với phép cộng dồn ta khởi tạo bằng 0, với phép nhân dồn ta khởi tạo bằng 1; dùng
long long vì giai thừa lớn rất nhanh.
Bài tập 2: menu lặp bằng do-while
Dùng vòng lặp do-while làm menu: hiện 4 lựa chọn (cộng, trừ, nhân, thoát), lặp lại cho
tới khi người dùng chọn thoát.
Lời giải chi tiết
do-while hợp với menu vì phải hiện menu ít nhất một lần rồi mới biết người dùng có muốn
thoát hay không. Điều kiện lặp là lựa chọn khác 4 (số thoát).
#include <stdio.h>
int main(void) {
int choice;
do {
printf("1.Add 2.Sub 3.Mul 4.Quit\n");
printf("Choose: ");
scanf("%d", &choice);
if (choice == 1) {
printf("You chose Add.\n");
} else if (choice == 2) {
printf("You chose Sub.\n");
} else if (choice == 3) {
printf("You chose Mul.\n");
}
} while (choice != 4); // repeat until the user picks Quit
printf("Bye.\n");
return 0;
}
Ý chính: khi cần chạy thân ít nhất một lần rồi mới xét điều kiện dừng, do-while gọn hơn
while vì không phải lặp lại lệnh hiển thị trước vòng lặp.
Bài tập 3: tam giác sao bằng vòng lồng nhau
Dùng vòng lặp lồng nhau in ra một tam giác sao có chiều cao n, dòng thứ i có i dấu
sao. Ví dụ với n bằng 4 thì in 1, 2, 3, 4 dấu sao trên bốn dòng.
Lời giải chi tiết
Vòng ngoài chạy theo số dòng, vòng trong in số sao đúng bằng chỉ số dòng hiện tại. Sau mỗi dòng phải xuống dòng bằng một lần in ký tự dòng mới.
#include <stdio.h>
int main(void) {
int n;
printf("Enter height: ");
scanf("%d", &n);
for (int i = 1; i <= n; i++) { // outer: row index
for (int j = 1; j <= i; j++) { // inner: print i stars
printf("*");
}
printf("\n"); // move to next row
}
return 0;
}
Ý chính: số lần chạy của vòng trong phụ thuộc biến đếm của vòng ngoài (j chạy tới i),
nhờ đó mỗi dòng dài dần. Đây là cách quen thuộc để vẽ hình bằng vòng lồng nhau.
Bài tập 4: tìm và sửa lỗi lệch một đơn vị
Đoạn mã dưới đây định tính trung bình cộng của một mảng 5 phần tử, nhưng nó chạy sai. Hãy chỉ ra lỗi, giải thích vì sao sai, rồi sửa lại.
#include <stdio.h>
int main(void) {
int a[5] = {10, 20, 30, 40, 50};
int sum = 0;
for (int i = 1; i <= 5; i++) // this line is wrong
sum += a[i];
printf("Average = %d\n", sum / 5);
return 0;
}
Lời giải chi tiết
Vòng lặp mắc hai lỗi lệch một đơn vị cùng lúc, và chúng che lấp lẫn nhau nên rất khó thấy.
Lỗi thứ nhất nằm ở điểm bắt đầu. Biến i khởi tạo bằng 1, nên phần tử a[0] chứa số 10
không bao giờ được cộng. Ta mất một số hạng ngay từ đầu.
Lỗi thứ hai nằm ở điều kiện dừng. Điều kiện i <= 5 cho phép i chạm tới 5, nên vòng
lặp cuối cùng đọc a[5]. Nhưng mảng chỉ có 5 phần tử với chỉ số hợp lệ từ 0 tới 4, nên
a[5] nằm ngoài mảng. C không chặn việc này, nó lặng lẽ đọc bất kỳ giá trị rác nào
đang nằm ở ô nhớ kế tiếp. Kết quả là sum bị cộng thêm một con số ngẫu nhiên, và trung
bình in ra sai hoàn toàn, thậm chí mỗi lần chạy lại cho một số khác nhau.
Điều nguy hiểm là chương trình vẫn chạy, không báo lỗi gì, nó chỉ trả lời sai. Lỗi cú
pháp thì trình biên dịch bắt hộ, còn lỗi này thì không ai bắt hộ bạn. Cách sửa là quay về
mẫu chuẩn: duyệt mảng n phần tử thì chỉ số chạy từ 0 tới n - 1, tức điều kiện phải là
i < n.
#include <stdio.h>
int main(void) {
int a[5] = {10, 20, 30, 40, 50};
int n = 5;
int sum = 0;
for (int i = 0; i < n; i++) // start at 0, stop before n
sum += a[i];
printf("Average = %.2f\n", (double)sum / n); // 30.00
return 0;
}
Ngoài việc sửa vòng lặp, lời giải còn đưa số 5 vào biến n thay vì rải nó ra ba chỗ
trong mã. Nhờ vậy khi mảng đổi kích thước, bạn chỉ sửa một dòng và không thể quên chỗ
nào. Phép chia cũng được ép sang số thực để trung bình có phần lẻ, vì sum / n với hai
số nguyên sẽ cắt cụt phần thập phân.
Tự kiểm tra
1) Trong vòng for tính tổng, ngay sau vòng cuối thì i mang giá trị bao nhiêu và vì sao vòng lặp dừng?
Sau vòng cuối, phần cập nhật làm i thành 5. Máy kiểm tra i <= n, tức 5 <= 4: sai,
nên thoát vòng. Vòng lặp luôn chạy thêm một lần kiểm tra điều kiện sai trước khi dừng.
2) Khác nhau cơ bản giữa while và do-while là gì?
while kiểm tra điều kiện trước khi chạy thân nên thân có thể chạy 0 lần. do-while
chạy thân trước rồi mới kiểm tra nên thân luôn chạy ít nhất 1 lần.
Câu hỏi tự kiểm
- 1Lệnh continue trong một vòng for nhảy tới đâu?
- 2Đoạn while (i < n) { if (a[i] < 0) continue; sum += a[i]; i++; } gặp vấn đề gì?
- 3Mẫu chuẩn để duyệt một mảng có n phần tử là gì?
- 4Với data = {3, -1, 5, 0, 9}, dùng continue để bỏ số âm và break khi gặp 0, tổng các số dương thu được là bao nhiêu?
Tóm tắt bài học
Mọi vòng lặp đều lắp từ ba bộ phận: điểm bắt đầu, điều kiện tiếp tục, và bước tiến làm
điều kiện dần trở nên sai. Ba cú pháp của C chỉ khác nhau ở chỗ đặt ba bộ phận ấy ở đâu.
for gom cả ba vào một dòng nên hợp khi đã biết số lần lặp và khó quên bước tiến. while
kiểm tra trước nên thân có thể chạy 0 lần, hợp khi việc dừng phụ thuộc dữ liệu. do-while
kiểm tra sau nên thân luôn chạy ít nhất một lần, hợp với vòng nhập liệu có kiểm tra.
break thoát hẳn khỏi vòng lặp, continue nhảy tới phần cập nhật rồi sang vòng kế. Nhớ
rằng trong while, một lệnh continue đặt phía trên dòng cập nhật sẽ làm chương trình
treo, vì biến điều khiển đứng yên mãi mãi.
Hai loại lỗi cần cảnh giác suốt đời. Lỗi lệch một đơn vị làm vòng lặp chạy thừa hoặc
thiếu đúng một vòng, thường do chọn nhầm giữa < và <=; thuộc lòng hai mẫu chuẩn
i = 0; i < n cho mảng và i = 1; i <= n cho toán học sẽ chặn được phần lớn. Vòng lặp
vô hạn làm chương trình treo, luôn do quên bước tiến, đi sai hướng, hoặc so sánh bằng
nhau trên số thực. Trước khi chạy một vòng lặp mới viết, hãy chỉ tay vào dòng lệnh làm
điều kiện tiến tới chỗ sai; chỉ không ra thì đừng bấm chạy.
Chọn vòng lặp theo bài: for khi biết trước số lần, while khi chỉ biết điều kiện dừng,
do-while khi cần chạy ít nhất một lần. break thoát hẳn, continue bỏ qua phần còn lại
của vòng hiện tại, và mọi vòng lặp đều phải làm điều kiện tiến dần tới sai để tránh treo.