Chuyển tới nội dung chính

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, whiledo-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.

Vòng for: biến đếm i thay đổi qua từng vòng C
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}
Ngăn xếp stack
main()
n= 4sum= 0
Bộ nhớ động heap
(trống)
Khởi tạo n = 4 và sum = 0. sum bắt đầu bằng 0 để cộng dồn dần.
1/12

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.

Coi chừng lặp vô hạn

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 breakcontinue

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.

continue trong while có thể gây treo máy

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à 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.

Ba dấu hiệu của lỗi lệch một đơn vị

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.

Kiểm tra bước tiến trước khi chạy

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ốngnên chọnvì sao
biết trước số lần lặpforba 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ừngwhilekhô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ầndo-whilekiể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átwhile (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ứ ii 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 whiledo-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

Kiểm tra nhanh: vòng lặp for, while, do-while0/4 đúngchưa trả lời
  1. 1Lệnh continue trong một vòng for nhảy tới đâu?
  2. 2Đoạn while (i < n) { if (a[i] < 0) continue; sum += a[i]; i++; } gặp vấn đề gì?
  3. 3Mẫu chuẩn để duyệt một mảng có n phần tử là gì?
  4. 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 <<=; 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.

Ghi nhớ

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.