break, continue, dan goto

Pernyataan lompat, pemakaian yang wajar, dan alasan goto dihindari.

Daftar isi

Pernyataan lompat (jump statement) memindahkan titik eksekusi ke tempat lain tanpa melewati urutan normal. Topik ini membahas break yang menghentikan perulangan, continue yang melewati satu putaran, dan goto yang melompat ke label bebas beserta alasan bentuk terakhir dihindari dalam kode modern. Program perbandingan pada bagian akhir menunjukkan bahwa versi terstruktur tanpa goto menyajikan alur yang lebih mudah diikuti.

break untuk menghentikan perulangan#

Pernyataan break menghentikan perulangan terdalam yang melingkupinya, kemudian eksekusi berlanjut pada pernyataan setelah perulangan itu. Bentuk ini tepat untuk pencarian yang boleh berhenti begitu jawabannya ditemukan, karena sisa putaran tidak membawa informasi baru. Penghentian dini juga menghemat kerja program ketika datanya panjang. Pada switch, break memegang peran yang sama, yaitu menutup satu case sebelum label berikutnya.

Program berikut mencari nilai ujian pertama yang melewati ambang tertentu. Variabel posisi diberi nilai awal -1 dan mempertahankannya selama belum ada hasil. Nilai -1 dipilih sebagai penanda karena tidak mungkin berupa indeks yang sah.

cpp cari-ambang.cpp
#include <iostream>

int main() {
    int nilai[] = {62, 74, 88, 91, 55, 79};
    int ambang = 85;
    int posisi = -1;

    for (int i = 0; i < 6; ++i) {
        if (nilai[i] >= ambang) {
            posisi = i;
            break;
        }
    }

    if (posisi != -1) {
        std::cout << "Nilai pertama di atas " << ambang << " adalah " << nilai[posisi]
                  << " pada indeks " << posisi << '\n';
    } else {
        std::cout << "Tidak ada nilai di atas ambang\n";
    }
    return 0;
}
text Keluaran
Nilai pertama di atas 85 adalah 88 pada indeks 2

Dua nilai pertama tidak memenuhi ambang, sedangkan 88 memenuhinya, sehingga break menghentikan perulangan pada indeks 2 dan sisa tiga nilai tidak diperiksa lagi. Tanpa break, perulangan tetap memeriksa seluruh enam nilai meskipun jawabannya sudah ada. Hasil akhirnya sama, tetapi pekerjaan yang dilakukan menjadi lebih banyak.

Yang dihentikan break hanyalah perulangan yang paling dalam. Program berikut menempatkan break di dalam perulangan bersarang dan menghitung banyaknya putaran yang benar-benar terjadi, sehingga pengaruhnya terlihat terukur.

cpp break-bersarang.cpp
#include <iostream>

int main() {
    int target = 7;
    int jumlahPutaran = 0;

    for (int baris = 0; baris < 3; ++baris) {
        for (int kolom = 0; kolom < 4; ++kolom) {
            ++jumlahPutaran;
            if (kolom == 2) {
                break;
            }
        }
    }
    std::cout << "Putaran: " << jumlahPutaran << '\n';
    return 0;
}
text Keluaran
Putaran: 9

Setiap baris selesai setelah tiga putaran karena break hanya keluar dari perulangan kolom, sedangkan perulangan baris tetap menyelesaikan tiga putarannya. Hasil sembilan putaran, bukan tiga putaran pada baris pertama saja, membuktikan bahwa break tidak berpengaruh terhadap perulangan di luarnya. Keluar dari semua tingkat sekaligus memerlukan mekanisme lain, dan salah satu polanya dibahas pada bagian goto di bawah.

continue untuk melewati satu iterasi#

Pernyataan continue membatalkan sisa badan perulangan untuk putaran yang sedang berjalan, kemudian langsung menuju putaran berikutnya. Pada perulangan for, pembaruan tetap dieksekusi setelah continue, sedangkan pada while pembaruan yang diletakkan setelah continue justru terlewat. Bentuk ini berguna untuk menyaring data yang tidak sah tanpa menyarangkan seluruh badan perulangan ke dalam satu kondisi besar. Keterbacaan meningkat karena maksud “lewati data ini” terlihat pada satu pernyataan.

Program berikut membaca catatan sensor suhu yang sebagian datanya rusak dan ditandai -1000. Nilai yang rusak dilewati, sedangkan data yang sah dijumlahkan untuk menghitung rata-ratanya.

cpp sensor-suhu.cpp
#include <iostream>

int main() {
    int suhu[] = {30, -1000, 32, 30, -1000, 32};
    int jumlahValid = 0;
    long long total = 0;

    for (int s : suhu) {
        if (s == -1000) {
            continue;
        }
        total += s;
        ++jumlahValid;
    }

    std::cout << "Data valid: " << jumlahValid << '\n';
    std::cout << "Rata-rata: " << total / jumlahValid << '\n';
    return 0;
}
text Keluaran
Data valid: 4
Rata-rata: 31

Dua data rusak dilewati continue sebelum mencapai perhitungan, sehingga hanya empat data yang terhitung dan rata-ratanya 31. Alternatif tanpa continue menempatkan seluruh perhitungan di dalam if (s != -1000), dan jumlah sarangnya bertambah setiap ada satu syarat penyaringan baru. Rata-rata dicetak dengan pembagian bulat, sehingga hasilnya berupa bilangan tanpa angka di belakang koma. Apabila hasil pecahan diperlukan, salah satu operand diubah menjadi double lebih dahulu, misalnya static_cast<double>(total) / jumlahValid.

goto dan alasan dihindari#

Pernyataan goto melompat ke label bernama di mana pun di dalam fungsi yang sama, maju maupun mundur. Lompatan bebas semacam ini membuat alur program tidak lagi mengikuti struktur teksnya, sehingga pembaca harus menelusuri label demi label untuk memahami jalurnya. Lompatan mundur menyembunyikan perulangan yang tidak terlihat di sintaks, sedangkan lompatan maju yang menyeberangi deklarasi variabel bahkan ditolak compiler. Karena itu, goto dihindari dalam kode modern, dan kebutuhan aslinya telah tergantikan oleh perulangan terstruktur, break, serta fungsi. Banyak kode warisan bergaya C masih menggunakannya untuk penanganan error berlabel, sehingga kemampuan membacanya tetap berguna.

Fragmen berikut memperlihatkan pola yang sebaiknya tidak diterapkan, yaitu keluar dari perulangan bersarang dengan goto. Fragmen ini tidak dieksekusi karena hanya mengilustrasikan polanya, sedangkan versi terstrukturnya dieksekusi tepat setelahnya. Label selesai menampung seluruh eksekusi yang lolos dari dua tingkat perulangan sekaligus.

cpp Contoh: keluar dari perulangan bersarang dengan goto
// tidak-dijalankan (ilustrasi anti-pola goto; versi terstruktur dieksekusi setelah ini)
#include <iostream>

int main() {
    int matriks[2][3] = {{4, 7, 1}, {9, 2, 5}};
    int dicari = 2;

    for (int baris = 0; baris < 2; ++baris) {
        for (int kolom = 0; kolom < 3; ++kolom) {
            if (matriks[baris][kolom] == dicari) {
                std::cout << "Ditemukan di (" << baris << ", " << kolom << ")\n";
                goto selesai;
            }
        }
    }
selesai:
    return 0;
}

Tugas yang sama diselesaikan program berikut tanpa goto. Kondisi !ditemukan ditambahkan pada kepala masing-masing perulangan, sehingga kedua tingkat berhenti segera setelah nilai ditemukan. Dengan susunan itu, tidak ada label tambahan dan setiap jalur keluar tetap berupa akhir badan yang normal. Hasilnya identik dengan versi berlabel, yaitu posisi nilai 2 yang terletak pada baris 1 dan kolom 1.

cpp cari-matriks.cpp
#include <iostream>

int main() {
    int matriks[2][3] = {{4, 7, 1}, {9, 2, 5}};
    int dicari = 2;
    bool ditemukan = false;
    int barisHasil = 0;
    int kolomHasil = 0;

    for (int baris = 0; baris < 2 && !ditemukan; ++baris) {
        for (int kolom = 0; kolom < 3 && !ditemukan; ++kolom) {
            if (matriks[baris][kolom] == dicari) {
                ditemukan = true;
                barisHasil = baris;
                kolomHasil = kolom;
            }
        }
    }

    if (ditemukan) {
        std::cout << "Ditemukan di (" << barisHasil << ", " << kolomHasil << ")\n";
    } else {
        std::cout << "Tidak ditemukan\n";
    }
    return 0;
}
text Keluaran
Ditemukan di (1, 1)

Versi terstruktur ini sedikit lebih panjang karena memerlukan variabel bendera ditemukan beserta tempat menampung hasilnya, tetapi seluruh alurnya terbaca dari atas ke bawah tanpa lompatan tersembunyi. Pemfaktoran ulang yang lebih lazim memindahkan pencarian ke sebuah fungsi tersendiri, dan return menghentikan semua tingkat perulangan sekaligus. Struktur semacam itu juga mudah diuji secara terpisah, sedangkan fungsi yang berisi label dan lompatan cenderung menuntut pengujian menyeluruh. Kebutuhan lompatan berlabel yang sungguh-sungguh jarang muncul dalam kode modern, sehingga kebiasaan yang dianjurkan adalah menuliskan strukturnya sejak awal.

Kesalahan umum#

Kesalahan berikut paling sering muncul ketika pernyataan lompat digunakan. Semuanya berkaitan dengan asumsi tentang seberapa jauh lompatan itu bekerja.

  1. Asumsi bahwa break menghentikan semua tingkat perulangan. break hanya keluar dari perulangan terdalam, sehingga perulangan luarnya tetap berjalan hingga kondisinya selesai. Gunakan kondisi bendera pada setiap tingkat, atau pindahkan logikanya ke fungsi yang mengembalikan hasil.
  2. continue yang diletakkan sebelum pembaruan pada while. Putaran yang terkena continue melewati pembaruan tersebut, sehingga kondisi tidak pernah berubah dan perulangan tidak berujung. Letakkan pembaruan sebelum continue, atau gunakan for yang pembaruannya tetap dijamin dieksekusi.
  3. goto yang menyeberangi deklarasi variabel ditolak compiler, sedangkan lompatan yang sah pun meninggalkan alur yang sulit diikuti. Perbaikannya bukan merapikan labelnya, melainkan mengganti lompatan dengan struktur perulangan atau fungsi yang setara.

Perbaikan untuk ketiganya bergerak ke arah yang sama, yaitu menjadikan jalur keluar program eksplisit pada teksnya. Kebiasaan ini sekaligus mengurangi kebutuhan pernyataan lompat pada kode yang sedang ditulis.

Ringkasan#

  • break menghentikan perulangan terdalam yang melingkupinya atau menutup satu case pada switch, tanpa pengaruh ke tingkat di luarnya.
  • continue melewati sisa satu putaran, dan pada for pembaruannya tetap dieksekusi.
  • goto melompat ke label bebas dan dihindari karena menyembunyikan alur, dengan kondisi bendera dan pemisahan ke fungsi sebagai penggantinya.

Referensi#