Vì sao chương trình cần rẽ nhánh
Mọi chương trình từ đầu tới giờ đều chạy thẳng một mạch: dòng 1, dòng 2, dòng 3. Chương trình thật thì phải ra quyết định. Mật khẩu có đúng không? Số này có âm không? Tệp đã mở được chưa? Câu lệnh if là cách C đặt câu hỏi và chỉ chạy code khi câu trả lời là có.
Điều kiện temperature > 30 được kiểm tra một lần. Nó đúng, nên thông báo được in ra. Dòng cuối nằm ngoài cặp ngoặc nhọn, nên luôn được in ra dù thế nào. Đổi 31 thành 18 rồi chạy lại: thông báo đầu biến mất, thông báo thứ hai vẫn còn.
Cú pháp: ngoặc tròn, ngoặc nhọn và không có dấu chấm phẩy
Câu lệnh if trong C có một hình dạng cố định:
if (condition) {
// runs when condition is nonzero
}
Có ba quy tắc nên nhớ sớm. Ngoặc tròn quanh điều kiện là bắt buộc. Không có từ khóa then. Và không có dấu chấm phẩy sau ngoặc tròn đóng: một dấu chấm phẩy lạc chỗ sẽ kết thúc câu lệnh ngay tại đó:
// BUG: the if does nothing; the printf always runs
if (x > 10);
{
printf("big\n");
}
Đoạn này biên dịch êm ru. Dấu ; là một câu lệnh rỗng trở thành thân của if, còn cặp ngoặc nhọn bên dưới chỉ là một khối lệnh độc lập luôn được thực thi. Biên dịch với gcc -Wall sẽ bắt được lỗi này.
Bản thân cặp ngoặc nhọn là tùy chọn khi chỉ có một câu lệnh, nhưng hãy cứ giữ chúng. Một if không có ngoặc nhọn về sau mọc thêm dòng thứ hai là nguồn bug kinh điển, vì trong C thụt lề không gom các câu lệnh lại với nhau.
else và else if
else cung cấp nhánh còn lại:
Khi có nhiều hơn hai kết quả, hãy nối chuỗi bằng else if. C kiểm tra từng điều kiện theo thứ tự và dừng ở điều kiện đúng đầu tiên:
Thứ tự ở đây cực kỳ quan trọng. Vì chuỗi dừng ở lần khớp đầu tiên, phép kiểm tra thứ hai chỉ bao giờ thấy các điểm dưới 90, đó là lý do score >= 80 không cần thêm && score < 90. Hãy viết chuỗi từ điều kiện chặt nhất tới lỏng nhất, nếu không mọi giá trị sẽ rơi vào nhánh đầu tiên.
else ở cuối là tùy chọn. Hãy thêm nó mỗi khi chuỗi điều kiện cần bao quát mọi trường hợp; thiếu nó nghĩa là "nếu không có gì khớp thì không làm gì", điều đó không sai nhưng nên là lựa chọn có chủ đích.
Đúng và sai: 0 là sai, mọi giá trị khác là đúng
Ngôn ngữ lõi của C không có kiểu boolean riêng trong điều kiện. Một if chỉ đơn giản hỏi biểu thức có khác 0 hay không.
Hai dòng cuối cho thấy nửa còn lại của quy tắc: các toán tử so sánh là những biểu thức bình thường, cho ra 1 khi đúng và 0 khi sai. Vì thế if (5 > 2) chạy được: thực chất nó là if (1).
Đây cũng là lý do -3 là đúng trong C. Quy tắc là khác 0, không phải dương. Nếu muốn biết một số có dương không, hãy viết if (n > 0), không phải if (n).
C99 thêm <stdbool.h> với kiểu bool thật sự cùng true và false, giúp code mới dễ đọc hơn. Bên dưới chúng vẫn là 1 và 0, nên quy tắc ở trên không thay đổi gì; xem trang Boolean trong C.
Các toán tử so sánh và logic bạn sẽ dùng trong điều kiện:
== equal to && logical AND (both sides true)
!= not equal to || logical OR (either side true)
< less than ! logical NOT
> greater than
<= less than or equal
>= greater than or equal
&& và || có tính đoản mạch (short-circuit): a && b không bao giờ tính b nếu a sai, và a || b không bao giờ tính b nếu a đúng. Đây là điều được đảm bảo, không phải một phép tối ưu, nên viết if (n != 0 && total / n > 5) là an toàn: phép chia không thể chạy khi n bằng 0.
Bug = và ==
Đây là lỗi phổ biến nhất khi viết điều kiện trong C:
// BUG: assigns 5 to x, then tests 5 (nonzero), so this ALWAYS runs
if (x = 5) {
printf("x is five\n");
}
C cho phép gán bên trong biểu thức, nên dòng này hợp lệ. Nó lưu 5 vào x, cho ra giá trị 5, và 5 khác 0: nhánh đó chạy ở mọi lần, còn x đã bị ghi đè một cách lặng lẽ.
Có hai cách phòng. Thứ nhất, luôn biên dịch với cảnh báo được bật:
gcc -Wall -Wextra program.c -o program
Cả GCC và clang đều báo "suggest parentheses around assignment used as truth value". Thứ hai, một số lập trình viên viết hằng số trước biến, if (5 == x), để lỡ gõ nhầm thành if (5 = x) thì sẽ là lỗi biên dịch rõ ràng thay vì một bug âm thầm. Phong cách đó tùy sở thích; còn bật cảnh báo thì không.
Lồng nhau
Một if có thể chứa một if khác. Lồng nhau thể hiện những điều kiện chỉ có nghĩa khi một điều kiện trước đó đã đúng:
Hai tầng thì còn dễ đọc. Bốn tầng thì không. Khi độ lồng sâu thêm, hãy tìm cách làm phẳng nó, thường là gộp điều kiện bằng &&, hoặc xử lý các trường hợp thất bại trước và return sớm:
// flatter: deal with the exceptions, then get on with the work
if (age < 18) { printf("Adults only.\n"); return 0; }
if (!hasTicket) { printf("Please buy a ticket.\n"); return 0; }
printf("Welcome in.\n");
else lơ lửng
Khi một if không có ngoặc nhọn lồng trong một if khác, else thuộc về if nào? Quy tắc của C: else gắn với if gần nhất chưa có cặp, bất kể code được thụt lề ra sao.
// The else belongs to the INNER if, despite the indentation
if (a > 0)
if (b > 0)
printf("both positive\n");
else
printf("a is not positive\n"); // WRONG - runs when a > 0 and b <= 0
Thụt lề cho thấy else đi với if bên ngoài. Trình biên dịch lại ghép nó với if bên trong. Ngoặc nhọn xóa bỏ hoàn toàn sự mơ hồ này, và đó là lý do thật sự để luôn dùng chúng:
if (a > 0) {
if (b > 0) {
printf("both positive\n");
}
} else {
printf("a is not positive\n");
}
Toán tử ba ngôi
Khi một nhánh chỉ tồn tại để chọn một giá trị, toán tử điều kiện ? : diễn đạt điều đó trong một biểu thức:
Đọc cond ? x : y là "nếu cond thì x, ngược lại thì y". Chỉ đúng một trong hai nhánh được tính, và cả biểu thức cho ra một giá trị, đó là lý do nó có thể được gán hoặc truyền làm đối số ở những chỗ mà một câu lệnh thì không thể.
Hãy dùng nó cho những lựa chọn giá trị đơn giản. Toán tử ba ngôi lồng nhau (a ? b : c ? d : e) hợp lệ nhưng hầu như luôn khó đọc hơn chuỗi else if mà nó thay thế.
Câu hỏi thường gặp
Viết câu lệnh if else trong C như thế nào?
Đặt điều kiện trong ngoặc tròn và code trong ngoặc nhọn: if (score >= 60) { printf("Pass\n"); } else { printf("Fail\n"); }. Khối else chỉ chạy khi điều kiện sai, và C không có từ khóa then.
Giá trị nào được coi là đúng trong câu lệnh if của C?
Mọi giá trị khác 0. Cú pháp lõi của C không có kiểu boolean riêng, nên if (n) chạy khi n là bất cứ giá trị nào trừ 0, còn if (!n) chỉ chạy khi n đúng bằng 0. Các toán tử so sánh như == và < trả về 1 cho đúng và 0 cho sai.
= và == trong C khác nhau thế nào?
== dùng để so sánh, = dùng để gán. Viết if (x = 5) sẽ gán 5 cho x rồi kiểm tra 5, một giá trị khác 0, nên nhánh đó luôn chạy. Đoạn code này biên dịch không lỗi, vì thế đây là bug phổ biến nhất của người mới khi viết điều kiện trong C; phần lớn trình biên dịch cảnh báo nó khi bật -Wall.
Toán tử ba ngôi trong C hoạt động thế nào?
condition ? valueIfTrue : valueIfFalse là một biểu thức cho ra một trong hai giá trị, nên nó có thể nằm trong phép gán hoặc làm đối số của printf: int max = a > b ? a : b;. Dùng nó để chọn giá trị, còn khi cần chạy các câu lệnh thì dùng if bình thường.