do while
Sau bài này bạn sẽ làm được
- Phân biệt do while với while về thời điểm kiểm tra điều kiện
- Dùng do while cho menu và cho việc kiểm tra dữ liệu nhập
- Nhớ dấu chấm phẩy bắt buộc ở cuối
- Giải thích vì sao macro nhiều câu lệnh dùng do while
do while chỉ khác while ở một điểm: điều kiện được kiểm tra sau khi thân đã chạy. Nghe nhỏ nhưng nó thay đổi hoàn toàn cách dùng, và cũng là lý do vòng lặp này xuất hiện trong mọi macro nhiều câu lệnh của C.
#Cú pháp
do {
/* thân vòng lặp */
} while (dieu_kien);#include <stdio.h>
int main(void)
{
int i = 1;
do {
printf("%d ", i);
i++;
} while (i <= 5);
printf("\n");
return 0;
}1 2 3 4 5
#Khác biệt với while
Với dữ liệu bình thường thì hai vòng lặp cho kết quả giống nhau. Khác biệt chỉ lộ ra khi điều kiện sai ngay từ đầu.
int i = 100;
while (i < 10) {
printf("A");
i++;
}
/* In 0 lần */
i = 100;
do {
printf("B");
i++;
} while (i < 10);
/* In 1 lần, vì thân chạy trước khi kiểm tra */| while | do while | |
|---|---|---|
| Kiểm tra điều kiện | Trước thân | Sau thân |
| Số lần chạy tối thiểu | 0 | 1 |
| Dấu chấm phẩy cuối | Không | Có |
| Dùng nhiều hay ít | Rất nhiều | Ít, chỉ vài tình huống |
#Hai ứng dụng chính
do while phù hợp đúng một loại tình huống: khi công việc chắc chắn phải làm ít nhất một lần, còn có lặp lại hay không thì phụ thuộc kết quả của lần đó.
Ứng dụng 1: menu
int chon;
do {
printf("\n1. Thêm\n2. Xóa\n3. Tìm\n0. Thoát\nChọn: ");
if (scanf("%d", &chon) != 1) {
int ch;
chon = -1;
/* Phải kiểm tra cả EOF, nếu không thì khi luồng vào kết thúc
vòng dọn bộ đệm này sẽ chạy vô hạn. */
while ((ch = getchar()) != '\n' && ch != EOF) {
}
if (ch == EOF) {
chon = 0; /* hết dữ liệu vào thì thoát menu */
}
}
switch (chon) {
case 1: them(); break;
case 2: xoa(); break;
case 3: tim(); break;
case 0: printf("Tạm biệt\n"); break;
default: printf("Lựa chọn không hợp lệ\n"); break;
}
} while (chon != 0);Menu phải hiện ra ít nhất một lần trước khi người dùng có cơ hội chọn thoát. Dùng while ở đây sẽ phải khởi tạo chon bằng một giá trị giả để điều kiện đúng ngay lần đầu, khá gượng ép.
int chon = -1; /* giá trị giả chỉ để vào được vòng lặp */
while (chon != 0) {
in_menu();
scanf("%d", &chon);
xu_ly(chon);
}int chon; /* không cần giá trị giả */
do {
in_menu();
scanf("%d", &chon);
xu_ly(chon);
} while (chon != 0);Ứng dụng 2: hỏi lại tới khi dữ liệu hợp lệ
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
long doc_so(const char *nhac, long min, long max)
{
char dong[128];
long v;
int hop_le;
do {
printf("%s", nhac);
if (!fgets(dong, sizeof dong, stdin)) {
exit(EXIT_FAILURE);
}
dong[strcspn(dong, "\n")] = '\0';
char *end;
v = strtol(dong, &end, 10);
hop_le = (end != dong) && (*end == '\0') && (v >= min) && (v <= max);
if (!hop_le) {
printf("Giá trị phải nằm trong khoảng %ld đến %ld.\n", min, max);
}
} while (!hop_le);
return v;
}Lần hỏi đầu tiên chắc chắn phải xảy ra, nên do while diễn đạt đúng ý định hơn. Đây là biến thể của hàm nhập số an toàn ở Bài 3.4 và Bài 3.5, viết lại bằng do while thay cho lệnh for vô hạn đã dùng ở Bài 3.5.
#Dùng trong macro
Có một chỗ mà do while gần như bắt buộc, và bạn đã gặp nó ở Bài 2.5: gói nhiều câu lệnh trong một macro.
#define HOAN_DOI(a, b) \
do { \
int t = (a); \
(a) = (b); \
(b) = t; \
} while (0)#define HOAN_DOI(a, b) { int t = (a); (a) = (b); (b) = t; }
if (x > y)
HOAN_DOI(x, y);
else
printf("đã đúng thứ tự");
/* Bung ra thành:
if (x > y)
{ ... } ; <- dấu chấm phẩy thành câu lệnh rỗng
else <- else giờ không có if đi kèm
printf(...);
Lỗi biên dịch: else without a previous if */#define HOAN_DOI(a, b) \
do { int t = (a); (a) = (b); (b) = t; } while (0)
if (x > y)
HOAN_DOI(x, y);
else
printf("đã đúng thứ tự");
/* Bung ra thành:
if (x > y)
do { ... } while (0);
else
printf(...);
Hoàn toàn hợp lệ. */Đây là lý do bạn sẽ thấy do while (0) ở khắp nơi trong mã nguồn của nhân Linux và của mọi thư viện C nghiêm túc. Nó không phải mẹo lạ mà là chuẩn mực.
Tự làm thử
- Viết chương trình đoán số: máy nghĩ một số từ 1 tới 100, người chơi đoán, máy báo cao hay thấp. Dùng
do whilevà đếm số lần đoán. - Viết menu máy tính bỏ túi có bốn phép toán, thoát bằng lựa chọn 0, dùng
do while. - Cài hàm
doc_soở trên, kiểm thử với các đầu vào sai như chữ cái, chuỗi rỗng, và số ngoài khoảng. - Viết cùng một chương trình bằng cả
whilevàdo while, chỉ ra chỗ nào phải thêm giá trị khởi tạo giả. - Định nghĩa macro
HOAN_DOIchỉ bằng ngoặc nhọn, dùng nó trong cặpif else, đọc thông báo lỗi, rồi sửa bằngdo while (0).
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
- do while kiểm tra điều kiện sau thân nên luôn chạy ít nhất một lần.
- Dấu chấm phẩy sau ngoặc của while là bắt buộc, đây là trường hợp duy nhất trong C.
- Hai ứng dụng chính là menu và hỏi lại tới khi dữ liệu hợp lệ.
- Macro nhiều câu lệnh phải bọc trong
do { } while (0)để gói thành một câu lệnh và nuốt được dấu chấm phẩy. - Trong mã thực tế do while ít gặp, chỉ dùng khi công việc chắc chắn phải làm ít nhất một lần.