Menu

Câu lệnh switch trong C++: case, fall-through và break

Giải thích câu lệnh switch trong C++: nhãn case, break và fall-through, nhánh default, gom nhiều case, dùng switch với enum và những bẫy khi khai báo biến bên trong case.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

switch dùng để làm gì

switch so sánh một giá trị với danh sách các lựa chọn cố định và chạy nhánh khớp. Khi bạn thấy mình đang viết một chuỗi if/else if dài, liên tục kiểm tra cùng một biến với các hằng số khác nhau, thì switch thể hiện ý định đó rõ ràng hơn, và thường được biên dịch thành một bảng nhảy (jump table) chạy nhanh hơn.

Nó chỉ kiểm tra bằng nhau với các hằng số xác định lúc biên dịch, trên một giá trị kiểu số nguyên. Nó không kiểm tra được khoảng giá trị, không so sánh được chuỗi và không kết hợp được nhiều điều kiện. Với những trường hợp đó, hãy dùng if/else.

switch cơ bản

Bạn đưa cho switch một giá trị, rồi liệt kê các nhãn case cho những giá trị bạn quan tâm. Mỗi nhánh kết thúc bằng break:

Giá trị 3 khớp với case 3:, nên "Wednesday" được in ra và break nhảy ra khỏi switch. Nhánh default là nhánh dự phòng: nó chạy khi không có case nào khớp. Nhánh này không bắt buộc, nhưng hãy thêm vào trừ khi bạn chắc chắn mọi giá trị đều đã được xử lý.

Lưu ý nhãn case chỉ là hằng số theo sau bởi dấu hai chấm, không phải điều kiện. Bạn viết case 3:, không bao giờ viết case day == 3:.

break và fall-through

Đây là cái bẫy quan trọng nhất của switch trong C++. Sau khi một case khớp, chương trình không dừng ở case kế tiếp mà cứ chạy thẳng xuống cho đến khi gặp break hoặc dấu ngoặc đóng. Hãy xem chuyện gì xảy ra khi thiếu các câu lệnh break:

Có thể bạn chỉ mong thấy "one". Thế nhưng cả bốn dòng đều được in ra: khớp case 1: đưa chương trình vào switch tại đó, rồi rơi xuống qua mọi nhãn bên dưới. Thêm break; sau mỗi nhánh là bạn có đúng một dòng như mong muốn. Quên break là nguồn gốc kinh điển của những bug kiểu "sao switch của mình chạy nhiều code thế?".

Cố ý dùng fall-through để gom case

Fall-through không phải lúc nào cũng là lỗi: đó là cách viết chuẩn để nhiều case dùng chung một thân lệnh. Để trống các case (không có câu lệnh, không có break) và tất cả sẽ chảy vào khối kế tiếp:

'A', 'B' và 'C' đều đến cùng dòng "Pass" vì hai case đầu trống và rơi xuống case thứ ba. Cách viết này gọn gàng và có chủ đích. Khi bạn thật sự muốn fall-through sau khi đã chạy một ít code, hãy ghi chú bằng comment, hoặc từ C++17 trở đi, dùng thuộc tính [[fallthrough]]; để nói với trình biên dịch "đúng, mình cố ý" và tắt cảnh báo fall-through.

Dùng switch với enum

switch kết hợp rất tự nhiên với enum, vì enum chính là "một trong một tập giá trị cố định". Trình biên dịch còn có thể cảnh báo nếu bạn quên xử lý một giá trị nào đó của enum:

Với enum class có phạm vi, bạn phải ghi đầy đủ tên cho từng nhãn (Direction::East). Vì mọi giá trị đều đã được xử lý nên không cần default, và nhiều trình biên dịch sẽ cảnh báo nếu sau này bạn thêm hướng thứ năm mà quên thêm case cho nó. Sự hỗ trợ đó của trình biên dịch là lý do lớn để ưu tiên switch thay vì chuỗi if/else khi làm việc với enum.

Một cái bẫy: khai báo biến bên trong case

Bạn không thể khai báo một biến có giá trị khởi tạo trong một case rồi để nó hiển thị ở các case khác mà không giới hạn phạm vi. Đây là một lỗi biên dịch thường gặp:

switch (x) {
    case 1:
        int n = 10;   // error: jump to case 2 bypasses this initialization
        cout << n;
        break;
    case 2:
        cout << "two";
        break;
}

Trình biên dịch từ chối vì khi nhảy vào case 2:, chương trình sẽ bỏ qua bước khởi tạo n trong khi n vẫn còn trong phạm vi. Cách sửa là bọc thân case trong cặp ngoặc nhọn riêng, để biến có khối lệnh của riêng nó:

Bất cứ khi nào một case cần biến cục bộ riêng, hãy cho nó cặp ngoặc nhọn. Cách này cũng ngăn biến lọt sang các case bên dưới.

Tiếp theo: vòng lặp for

switch và if giúp chương trình chọn đoạn code nào sẽ chạy một lần. Nhưng rất nhiều việc đòi hỏi làm cùng một thao tác nhiều lần: đếm, duyệt qua một danh sách, lặp lại cho đến khi điều kiện thay đổi. Vòng lặp for là công cụ chủ lực cho việc đó, và đó là nội dung của trang tiếp theo.

Câu hỏi thường gặp

Khi nào nên dùng switch thay cho if-else trong C++?

Dùng switch khi bạn so sánh một giá trị kiểu số nguyên với nhiều lựa chọn hằng cố định, ví dụ một lựa chọn menu, số thứ tự của ngày hoặc một enum. Cách viết này rõ ràng hơn một chuỗi if/else if dài và giúp trình biên dịch tối ưu việc rẽ nhánh. Hãy giữ if/else khi điều kiện liên quan đến khoảng giá trị (x > 10), số thực, chuỗi hoặc nhiều biến, vì switch không làm được những việc đó.

Vì sao cần break trong câu lệnh switch của C++?

Khi một case khớp, C++ tiếp tục thực thi các case phía sau cho đến khi gặp break hoặc hết khối switch. Hiện tượng này gọi là fall-through. break sẽ dừng nó lại. Quên break là một bug kinh điển: bạn khớp case 1: nhưng vô tình chạy luôn code của case 2, case 3 và default.

Có thể dùng switch với những kiểu dữ liệu nào trong C++?

Chỉ các kiểu số nguyên hoặc kiểu liệt kê: int, char, short, long, bool, enum có phạm vi hoặc không, và mọi thứ chuyển được sang một trong các kiểu đó. Bạn không thể switch trên double, float hay std::string; với những kiểu này hãy dùng if/else. Mỗi nhãn case phải là một hằng số xác định lúc biên dịch.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu