Sebelum debugger dan sanitizer bekerja, compiler sudah berjalan lebih dulu di garis depan — dan bila kita izinkan, ia melaporkan puluhan pola kode yang “sah secara bahasa tetapi nyaris pasti salah”. Peringatan (warning) berbeda dari error: compiler tetap menghasilkan executable, sehingga mudah terlewat atau diabaikan begitu saja. Kebiasaan profesional yang ingin kita bangun di chapter ini adalah memperlakukan peringatan sebagai pekerjaan rumah yang harus lunas sebelum program dijalankan, dimulai dari tiga flag standar: -Wall, -Wextra, dan -Wpedantic.

Mengaktifkan Peringatan Secara Penuh #

Secara bawaan, g++ cukup diam. Flag di bawah ini yang akan kita pakai sepanjang course — tambahkan -Wpedantic bila kode perlu dipindah antar compiler:

bash
g++ -std=c++23 -Wall -Wextra -Wpedantic program.cpp -o program
  • -Wall: bukan “all warnings” secara harfiah, tetapi sekumpulan peringatan yang paling sering menandai bug nyata: variabel terpakai tanpa inisialisasi, lupa return, pembandingan tipe tak sepadan, dan sebagainya.
  • -Wextra: lapisan kedua — lebih sensitif dan kadang berisik, misalnya parameter function yang tidak pernah dipakai.
  • -Wpedantic: menolak ekstensi khusus compiler (GNU extension) agar kode tetap standar ISO dan portabel.

Berkas pertama kita sengaja memuat tiga pelanggaran yang paling umum di kode pemula. Perhatikan bahwa semuanya sah secara sintaks — tanpa flag, g++ 14.2 mengompilasinya tanpa sepatah kata pun:

cpp sebelum.cpp — tiga peringatan yang tersembunyi
// sebelum.cpp — sengaja penuh peringatan; kompilasi dengan -Wall -Wextra
// untuk melihat apa saja yang compiler temukan tanpa menghentikan build.

#include <iostream>
#include <vector>

int jumlahkan(const std::vector<int>& data) {
    int total = 0;
    for (std::size_t i = 0; i < data.size(); ++i) {
        total += data[i];
    }
    // lupa return total — -Wreturn-type akan muncul di sini
}

int main() {
    int kunciRahasia = 42;                       // -Wunused-variable
    std::vector<int> nilai = {4, 8, 15, 16, 23, 42};
    std::size_t jumlahNilai = nilai.size();
    if (jumlahNilai > -1) {                      // -Wsign-compare
        std::cout << "jumlah elemen: " << jumlahNilai << "\n";
    }
    return 0;
}

Kompilasi berikut adalah hasil nyata dari g++ -std=c++23 -Wall -Wextra sebelum.cpp -o sebelum di g++ 14.2 — perhatikan baris, kolom, dan tag -W... di ujung tiap pesan:

text
sebelum.cpp: In function ‘int jumlahkan(const std::vector<int>&)’:
sebelum.cpp:13:1: warning: no return statement in function returning non-void [-Wreturn-type]
   13 | }
      | ^
sebelum.cpp: In function ‘int main()’:
sebelum.cpp:19:21: warning: comparison of integer expressions of different signedness: ‘std::size_t’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
   19 |     if (jumlahNilai > -1) {                      // -Wsign-compare
      |         ~~~~~~~~~~~~^~~~
sebelum.cpp:16:9: warning: unused variable ‘kunciRahasia’ [-Wunused-variable]
   16 |     int kunciRahasia = 42;                       // -Wunused-variable
      |         ^~~~~~~~~~~~

Membedah Tiga Kelas Peringatan #

  1. -Wreturn-type (baris 13): function jumlahkan bertipe int tetapi bisa berakhir tanpa return. Bila pemanggil memakai nilai kembaliannya, itu undefined behavior — program boleh membaca “nilai sampah” dari register. Ini peringatan yang hampir selalu berbahaya; perbaiki dengan menambah return total;.
  2. -Wsign-compare (baris 19): jumlahNilai bertanda tak-positif (std::size_t) dibandingkan dengan -1 yang bertanda positif-negatif (int). Dalam perbandingan campuran, compiler mengubah -1 menjadi angka raksasa, sehingga jumlahNilai > -1 tak mungkin salah — kondisi yang berlawanan dengan niat penulisnya. Bug perbandingan tipe seperti ini pernah merembes ke sistem sungguhan bertahun-tahun tanpa ketahuan.
  3. -Wunused-variable (baris 16): kunciRahasia diberi nilai tetapi tak pernah dibaca. Kelas ini minor — biasanya sisa refaktor — tetapi kadang menjadi petunjuk ada variabel yang seharusnya dipakai dan salah ketik nama.

Peringatan yang Baru Muncul Saat Optimasi #

Sebagian analisis butuh data aliran yang baru tersedia pada tahap optimasi. Program kecil ini lolos bersih di -O0, lalu dihajar compiler di -O2:

cpp bagi.cpp — satu jalur lupa mengisi variabel
#include <iostream>

int bagi(int a, int b) {
    int hasil;
    if (b != 0) {
        hasil = a / b;
    }
    return hasil;   // jalur b == 0 tidak pernah mengisi hasil
}

int main() {
    std::cout << bagi(10, 2) << "\n";
    return 0;
}
text Output g++ -std=c++23 -Wall -Wextra -O2 bagi.cpp (nyata)
bagi.cpp: In function ‘int bagi(int, int)’:
bagi.cpp:8:12: warning: ‘hasil’ may be used uninitialized [-Wmaybe-uninitialized]
    8 |     return hasil;   // jalur b == 0 tidak pernah mengisi hasil
      |            ^~~~~
bagi.cpp:4:9: note: ‘hasil’ was declared here
    4 |     int hasil;
      |         ^~~~~

Pelajaran dua lapis: pertama, variabel yang hanya terisi di sebagian jalur adalah perangkap klasik — isi nilai di deklarasi (int hasil = 0;) dan peringatan itu tidak akan pernah muncul. Kedua, build pengembangan dan build rilis sebaiknya sama-sama dikompilasi dengan flag peringatan penuh, karena keduanya menemukan set yang tidak identik. Dua peringatan lain yang perlu Anda kenali dari kelas “butuh konteks”: -Woverflow (nilai literal terpotong saat konversi, misalnya char c = 300; berubah menjadi 44) dan -Wpedantic, yang menampik ekstensi GNU seperti case range case 1 ... 5: dengan pesan range expressions in switch statements are non-standard.

-Werror: Peringatan yang Tidak Bisa Diabaikan #

Pada codebase bersama, cara paling efektif menegakkan disiplin adalah mengubah peringatan menjadi error sehingga build gagal keras:

text Output g++ -std=c++23 -Wall -Wextra -Werror sebelum.cpp (nyata)
sebelum.cpp: In function ‘int jumlahkan(const std::vector<int>&)’:
sebelum.cpp:13:1: error: no return statement in function returning non-void [-Werror=return-type]
   13 | }
      | ^
sebelum.cpp: In function ‘int main()’:
sebelum.cpp:19:21: error: comparison of integer expressions of different signedness: ‘std::size_t’ {aka ‘long unsigned int’} and ‘int’ [-Werror=sign-compare]
   19 |     if (jumlahNilai > -1) {                      // -Wsign-compare
      |         ~~~~~~~~~~~~^~~~
sebelum.cpp:16:9: error: unused variable ‘kunciRahasia’ [-Werror=unused-variable]
   16 |     int kunciRahasia = 42;                       // -Wunused-variable
      |         ^~~~~~~~~~~~
cc1plus: all warnings being treated as errors

Kompilasi berhenti, tidak ada executable yang dihasilkan, dan tiap pesan kini berakhir di kategori error. Dalam pipeline CI, kombinasi -Wall -Wextra -Werror adalah pagar pertama kualitas kode sebelum sanitizers dan unit test bekerja. Bila satu peringatan tertentu terbukti palsu, kecualikan secara eksplisit dengan -Wno-<nama> atau pragma #pragma GCC diagnostic ignored — jangan matikan seluruh -Wall karena satu kasus.

Versi Bersih: Nol Peringatan #

File berikut memperbaiki ketiga masalah sebelum.cpp dan lolos -Wall -Wextra -Wpedantic tanpa satu pesan pun:

cpp
// sesudah.cpp — versi bersih: lolos -Wall -Wextra tanpa satu pun peringatan.

#include <iostream>
#include <vector>

int jumlahkan(const std::vector<int>& data) {
    int total = 0;
    for (const int& nilai : data) {
        total += nilai;
    }
    return total;   // perbaikan 1: nilai kembalian tidak lagi terlupa
}

int main() {
    std::vector<int> nilai = {4, 8, 15, 16, 23, 42};
    const std::size_t jumlahNilai = nilai.size();

    std::cout << "jumlah elemen: " << jumlahNilai << "\n";
    std::cout << "total: " << jumlahkan(nilai) << "\n";

    // perbaikan 3: perbandingan memakai tipe bertanda yang setara
    if (static_cast<int>(jumlahNilai) > 0) {
        std::cout << "data tidak kosong\n";
    }
    return 0;
}

Output yang Dihasilkan:

text
jumlah elemen: 6
total: 108
data tidak kosong

Tiga perbaikannya patut dicatat sebagai pola: return total; menutup jalur keluar function; variabel yang tidak dipakai dihapus sama sekali, bukan diberi komentar; dan perbandingan ditulis ulang agar kedua sisi punya tipe sepadan — static_cast<int>(jumlahNilai) > 0 aman karena jumlah elemen std::vector tidak mungkin melebihi rentang int dalam program wajar. Perhatikan juga loop yang kini memakai range-based for: menghapus indeks menghilangkan seluruh kelas soal tipe indeks.

Memilah Peringatan: Berbahaya vs Minor #

Tidak semua peringatan setara pentingnya, dan mengetahui urutan prioritasnya menjaga fokus saat laporan panjang menyambut Anda:

TagArti ringkasPrioritas
-Wreturn-type, -Wmaybe-uninitializedjalur eksekusi menghasilkan nilai tidak tentu (UB)berbahaya — tangani hari ini
-Wsign-compareperbandingan campuran tanda bisa membalik logikatinggi
-Woverflownilai literal/konversi terpotongtinggi
-Wunused-variable, -Wunused-parametersisa kode matiminor — kebersihan
-Wpedanticmemakai ekstensi non-standarminor — isu portabilitas

Peringatan compiler hanya bisa melihat satu berkas sekaligus dan tidak pernah menjalankan program. Bug yang hanya muncul saat eksekusi — pelanggaran memori, misalnya — butuh lapisan alat berikutnya: sanitizer runtime, yang kita kenal pada chapter berikutnya.

Referensi: GCC — Warning Options .