Chapter sebelumnya ditutup dengan pertanyaan: bagaimana bila kita memilih aksi berdasarkan nilai tetap dari satu variabel — hari ke-1, ke-2, ke-3, dan seterusnya? Rantai else if mampu menyelesaikannya, tetapi switch memberi bentuk yang lebih ringkas dan lebih jelas maksudnya: satu ekspresi yang diperiksa, lalu daftar label case sebagai pintu masuk. Namun switch punya mekanisme internal yang berbeda dari if, dan perbedaan itulah yang memunculkan aturan wajib break serta fenomena fallthrough. Pada Chapter ini kita membedah semuanya, termasuk tipe data apa saja yang boleh di-switch dan yang tidak.
Struktur switch, case, dan break #
Program berikut menerjemahkan nomor hari menjadi nama hari. Semua cabang menguji satu variabel yang sama, hari, terhadap nilai-nilai tetap:
#include <iostream>
int main() {
int hari = 3;
switch (hari) {
case 1:
std::cout << "Senin\n";
break;
case 2:
std::cout << "Selasa\n";
break;
case 3:
std::cout << "Rabu\n";
break;
case 4:
std::cout << "Kamis\n";
break;
case 5:
std::cout << "Jumat\n";
break;
default:
std::cout << "Nomor hari tidak dikenal\n";
break;
}
return 0;
}Output yang Dihasilkan:
RabuMekanismenya berbeda mendasar dari if:
- Baris 7 (
switch (hari)): ekspresi di dalam tanda kurung dievaluasi sekali, menghasilkan nilai 3. Ekspresi ini harus bertipe bilangan bulat,char, atau enum — bukanboolsemata dan bukan tipe titik-mengambang. - Baris 11 (
case 3:): compiler mencari label yang nilainya sama dengan hasil ekspresi, lalu melompat ke sana. Perbandingannya eksak dan tanpa rentang; tidak adacase >= 3. - Baris 12-13: pernyataan setelah label dijalankan seperti biasa.
- Baris 14 (
break;): perintah ini yang keluar dari blokswitch. Tanpabreak, eksekusi MENERUS turun ke pernyataan berikutnya — perilaku yang bernama fallthrough dan segera kita bedah.
Label default adalah pintu untuk semua nilai yang tidak cocok dengan label manapun — padanan else dalam rantai. Posisinya bebas (tidak harus di bawah), tetapi kebiasaan umum menaruhnya paling akhir. Perlakukan default sebagai penangkap kejutan: tanpa itu, nilai tak terduga berjalan melewati switch dalam diam.
Fallthrough: Perilaku Bawaan yang Berbahaya #
Inilah bagian yang paling sering menjerat pemula. case bukan blok terpisah seperti kurung kurawal; ia hanyalah LABEL — titik lompat. Setelah masuk lewat satu label, eksekusi mengalir lurus menuruni semua pernyataan berikutnya, masuk ke case lain tanpa menanyakan nilai labelnya, sampai bertemu break atau akhir switch. Contoh berikut justru MEMANFAATkan perilaku itu secara sengaja, dengan menandainya eksplisit:
#include <iostream>
int main() {
int level = 1;
switch (level) {
case 1:
std::cout << "Mode dasar aktif\n";
[[fallthrough]]; // sengaja meneruskan ke case berikutnya
case 2:
std::cout << "Fitur lanjutan ikut aktif\n";
break;
case 3:
std::cout << "Akses penuh\n";
break;
}
return 0;
}Output yang Dihasilkan:
Mode dasar aktif
Fitur lanjutan ikut aktifNilai level adalah 1, jadi eksekusi masuk lewat case 1, mencetak “Mode dasar aktif”, lalu — karena tidak ada break di situ — menerus ke pernyataan milik case 2 dan mencetak baris kedua. Atribut [[fallthrough]] (dibaca compiler sebagai penanda, bukan pernyataan) menyatakan “lompatan ini memang disengaja”. Flag -Wall biasanya memberi peringatan bila ada case yang berakhir tanpa break; atribut ini meredam peringatan pada titik yang memang benar.
Bentuk label bertumpuk adalah satu-satunya fallthrough yang kita sarankan, karena niatnya terbaca tanpa perlu berpikir:
#include <iostream>
int main() {
char huruf = 'e';
switch (huruf) {
// Beberapa label boleh menunjuk ke blok yang sama
case 'a':
case 'i':
case 'u':
case 'e':
case 'o':
std::cout << huruf << " adalah vokal\n";
break;
default:
std::cout << huruf << " adalah konsonan\n";
break;
}
return 0;
}Output yang Dihasilkan:
e adalah vokalContoh ini sekaligus menunjukkan bahwa switch bekerja pada char: lima label bernilai karakter bertumpuk menuju satu blok, dan case 'e' adalah yang dicocokkan. Referensi lengkap sintaksnya tersedia di cppreference
.
Deklarasi Variabel di Dalam case #
Karena case adalah label lompatan, mendeklarasikan variabel dengan inisialisasi langsung di dalamnya berpotensi bermasalah: eksekusi bisa melompat KE label lain sambil MELEWATI deklarasi tersebut, dan compiler menolaknya. Coba potongan ini — compiler kami benar-benar menolaknya:
switch (pilihan) {
case 1:
int hasil = 10; // deklarasi tanpa blok
std::cout << hasil << "\n";
break;
case 2:
std::cout << "dua\n"; // lompatan ke sini melewati deklarasi hasil
break;
}Pesan galat g++-nya: error: jump to case label dengan catatan crosses initialization of 'int hasil'. Perbaikannya sederhana: bungkus isi case dengan kurung kurawal sehingga variabel punya blok sendiri — case 1: { int hasil = 10; ... break; }. Dengan blok, deklarasi tidak dapat dilewati dari luar dan masalahnya berakhir.
Batasan: Tipe dan String #
Dua batasan praktis perlu kita catat. Pertama, switch hanya menerima tipe bilangan bulat, char, atau enum (tipe khusus untuk kumpulan nilai bernama — dibahas pada Chapter Enum di Section Struktur dan Tipe Data Komposit); double dan std::string tidak boleh menjadi ekspresi switch maupun label. Kedua — dan ini yang paling sering ditanyakan — switch pada std::string TIDAK didukung oleh bahasa, karena string bukan tipe integral. Untuk memilih aksi berdasarkan teks, kembali ke rantai else if:
std::string peran = "admin";
if (peran == "admin") {
// aksi admin
} else if (peran == "editor") {
// aksi editor
} else {
// aksi bawaan
}switch menuntut kehati-hatian pada dua titik: break dan deklarasi di dalam case. Keduanya berakar dari fakta yang sama — case adalah label, bukan blok. Kita akan bertemu break lagi pada Chapter break dan continue, kali ini di dalam loop, tempat fungsinya sama persis: keluar dari struktur kendali yang melingkupinya. Sebelum itu, kita butuh struktur yang bisa mengulang pekerjaan: for.