Toán tử ba ngôi
Sau bài này bạn sẽ làm được
- Viết biểu thức điều kiện đúng cú pháp
- Biết khi nào toán tử ba ngôi làm mã dễ đọc hơn if
- Nhận ra bẫy hai nhánh khác kiểu bị ép ngầm về một kiểu
Đây là toán tử duy nhất trong C có ba toán hạng. Nó rút gọn được một khối if else chỉ để gán giá trị thành một dòng. Dùng đúng chỗ thì mã gọn và rõ, dùng sai chỗ thì mã trở nên khó đọc hơn hẳn.
#Cú pháp
dieu_kien ? gia_tri_neu_dung : gia_tri_neu_saiToàn bộ cấu trúc là một biểu thức có giá trị, không phải câu lệnh. Đây là khác biệt cốt lõi so với if else.
int max = (a > b) ? a : b;
printf("%s\n", n % 2 == 0 ? "Chẵn" : "Lẻ");
const char *trang_thai = ok ? "thành công" : "thất bại";
int abs_x = x < 0 ? -x : x;#Khi nào nên dùng
Gán một giá trị đơn giản
int max = a > b ? a : b;
int dau = x < 0 ? -1 : 1;
int hop_le = (tuoi >= 0 && tuoi <= 150);Chèn vào giữa một biểu thức khác
printf("Bạn có %d quả táo\n", n);
printf("Trạng thái: %s\n", dang_chay ? "đang chạy" : "đã dừng");
/* Chọn đơn vị hiển thị */
printf("%.1f %s\n",
kich_thuoc >= 1024 ? kich_thuoc / 1024.0 : kich_thuoc,
kich_thuoc >= 1024 ? "MB" : "KB");Kẹp giá trị vào một khoảng
int kep(int x, int min, int max)
{
return x < min ? min : (x > max ? max : x);
}
/* Hoặc dùng macro, nhớ bọc ngoặc đầy đủ */
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))#Khi nào không nên
Lồng nhiều tầng
x = a > b ? (c > d ? 1 : 2) : (e > f ? 3 : 4);
/* Phải đọc đi đọc lại mới hiểu. */if (a > b) {
x = (c > d) ? 1 : 2;
} else {
x = (e > f) ? 3 : 4;
}Một tầng thì rõ, hai tầng thì bắt đầu khó, ba tầng thì gần như không ai đọc được. Ngoại lệ duy nhất được chấp nhận rộng rãi là chuỗi phân loại có cấu trúc lặp lại đều đặn, và ngay cả khi đó thì nhiều dự án vẫn cấm.
Dùng thay cho câu lệnh
a > b ? printf("a lớn hơn") : printf("b lớn hơn");
/* Hợp lệ nhưng sai tinh thần.
Toán tử ba ngôi sinh ra để TÍNH GIÁ TRỊ,
không phải để chọn hành động. */if (a > b) {
printf("a lớn hơn");
} else {
printf("b lớn hơn");
}Khi hai nhánh có tác dụng phụ
/* Khó đọc và dễ gây hiểu nhầm */
ok ? ghi_log_thanh_cong() : ghi_log_that_bai();
/* Rõ ràng hơn */
if (ok) {
ghi_log_thanh_cong();
} else {
ghi_log_that_bai();
}#Bẫy hai nhánh khác kiểu
Kiểu của cả biểu thức được quyết định bằng cách áp dụng quy tắc chuyển đổi số học thông thường lên hai nhánh, giống như với toán tử hai ngôi. Điều này xảy ra ngay cả khi nhánh đó không được chọn.
printf("%d\n", 1 ? 1 : 2.0);
/* Nhánh thứ hai là double, nên CẢ BIỂU THỨC có kiểu double.
Giá trị là 1.0 chứ không phải 1.
In bằng %d là hành vi không xác định. */#Tổng kết Phần 1
Bạn vừa hoàn thành Phần 1. Đây là những gì đã tích lũy được qua bốn chương.
- Chương 2: cấu trúc một file nguồn, biến là vùng nhớ có tên, kích thước các kiểu không cố định theo chuẩn,
sizeoftính lúc biên dịch, và ba cách tạo hằng số. - Chương 3: cơ chế đệm quyết định khi nào chữ hiện ra, năm thành phần của một đặc tả định dạng, và cách nhập liệu an toàn bằng
fgetskèmstrtol. - Chương 4: chia số nguyên cắt phần dư, năm lỗi so sánh kinh điển, đánh giá ngắn mạch, và bảng độ ưu tiên với năm cái bẫy.
- Chương 5: luôn dùng ngoặc nhọn, thứ tự điều kiện quyết định kết quả, guard clause làm phẳng mã, và fallthrough của
switch.
Phần 2 sẽ đi vào vòng lặp và tư duy thuật toán. Đó là nơi bạn bắt đầu giải được những bài toán thật sự, và cũng là nơi bài học về độ phức tạp bắt đầu có ý nghĩa thực tế.
Tự làm thử
- Viết hàm trả về giá trị lớn nhất trong ba số, chỉ dùng toán tử ba ngôi.
- Viết chương trình in ra một số là chẵn hay lẻ, âm hay dương, dùng toán tử ba ngôi bên trong
printf. - Viết hàm
kepgiới hạn giá trị vào một khoảng, rồi viết lại bằngif elsevà so sánh. - Chạy
printf("%d\n", 1 ? 1 : 2.0)và giải thích kết quả. Sau đó sửa cho đúng. - Viết biểu thức
int x = a > b ? a : b + 1;rồi kiểm chứng độ ưu tiên bằng các giá trị cụ thể. - Viết một chương trình tổng hợp dùng lại kiến thức cả Phần 1: menu máy tính có nhập liệu an toàn, xử lý lỗi đầy đủ, dùng
switchcho lựa chọn, và in kết quả có định dạng đẹp.
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
- Toán tử ba ngôi là toán tử duy nhất của C có ba toán hạng, và nó là biểu thức chứ không phải câu lệnh.
- Chỉ một trong hai nhánh được đánh giá, nên nó an toàn khi một nhánh có thể gây lỗi.
- Nó cho phép khởi tạo biến ngay lúc khai báo, kể cả biến const.
- Dùng khi chọn một giá trị, dùng if else khi chọn một hành động.
- Kiểu của cả biểu thức lấy từ cả hai nhánh, nên trộn kiểu khác nhau sinh ra bẫy ép kiểu ngầm.
- Độ ưu tiên rất thấp nên hầu như luôn cần ngoặc khi nằm trong biểu thức lớn hơn.