Compiler gcc membedakan dua tingkat keparahan pesan: error yang menghentikan kompilasi, dan warning yang hanya memberi catatan tetap mengizinkan program dibangun. Banyak bug halus—buffer overflow, format specifier salah, variabel tak terpakai—tidak secara teknis melanggar sintaks C, sehingga compiler hanya mengeluarkan warning. Membuang warning dengan alasan “tetap compile” adalah kebiasaan berbahaya: pesan-pesan tersebut hampir selalu menandai cacat nyata. Pada chapter ini kita akan mempelajari flag warning utama (-Wall, -Wextra, -Wpedantic), perilaku -Werror yang menaikkan warning menjadi error, dan cara membaca beberapa warning paling produktif (-Wunused-variable, -Wsign-compare, -Wformat, -Wreturn-type).

Tiga Flag Warning Utama #

-Wall #

Flag -Wall mengaktifkan sekumpulan warning yang dianggap paling berguna dan minim false positive. Nama “all” menyesatkan—flag ini tidak mengaktifkan seluruh warning gcc, hanya yang sudah dikurasi tim gcc sebagai aman untuk production code. Berikut beberapa warning yang masuk grup -Wall:

  • -Wunused-variable: variabel dideklarasikan tetapi tidak pernah dipakai.
  • -Wunused-function: fungsi static tidak pernah dipanggil.
  • -Wformat: format specifier printf/scanf tidak cocok dengan tipe argumen.
  • -Wreturn-type: fungsi non-void punya path yang tidak mengembalikan nilai.
  • -Wmaybe-uninitialized: variabel dipakai sebelum diberi nilai awal.

-Wextra #

-Wextra menambahkan warning yang lebih bertele-tele dan terkadang noisy pada kode lama, tetapi sangat berguna untuk kode baru. Contoh warning yang hanya aktif lewat -Wextra:

  • -Wsign-compare: membandingkan signed dan unsigned integer (potensi bug ketika i < n dengan i bertanda dan n tak bertanda).
  • -Wempty-body: body if/while kosong akibat titik koma if (x); ....
  • -Wmissing-field-initializers: struct tidak diinisialisasi penuh.
  • -Wunused-parameter: parameter fungsi tidak dipakai.

-Wpedantic #

Flag -Wpedantic (sebelumnya -pedantic) menonaktifkan ekstensi non-standar gcc dan memunculkan warning untuk konstruksi yang secara teknis valid tetapi melanggar standar ISO C ketat. Flag ini wajib dipakai bila kita ingin kode portabel ke compiler lain (clang, MSVC, vendor compiler tertentu).

Contoh Nyata: Variabel Tak Terpakai #

c
/* File: /tmp/test_warn_unused.c */
#include <stdio.h>
int main(void) {
    int x = 10;
    int y = 20;  /* tidak dipakai */
    printf("x = %d\n", x);
    return 0;
}

Tanpa -Werror, kompilasi masih berhasil:

bash
gcc -std=c11 -Wall -Wextra /tmp/test_warn_unused.c -o /tmp/test_warn_unused

Output yang Dihasilkan:

text
/tmp/test_warn_unused.c: In function 'main':
/tmp/test_warn_unused.c:4:9: warning: unused variable 'y' [-Wunused-variable]
    4 |     int y = 20;  /* tidak dipakai */
      |         ^

Saat kita menambah -Werror, warning yang sama menjadi error dan kompilasi gagal:

bash
gcc -std=c11 -Wall -Wextra -Wpedantic -Werror /tmp/test_warn_unused.c -o /tmp/test_warn_unused2

Output yang Dihasilkan:

text
/tmp/test_warn_unused.c: In function 'main':
/tmp/test_warn_unused.c:4:9: error: unused variable 'y' [-Werror=unused-variable]
    4 |     int y = 20;  /* tidak dipakai */
      |         ^
cc1: all warnings being treated as errors

Mari kita bedah perubahan kunci pada output di atas:

  1. Kata kunci error menggantikan warning: identitas pesan berubah dari catatan menjadi galat fatal. gcc menambahkan suffix [-Werror=unused-variable] agar kita tahu warning mana yang dinaikkan tingkatnya.
  2. Baris cc1: all warnings being treated as errors: baris penutup dari preprocessor front-end cc1 mengkonfirmasi bahwa flag -Werror aktif dan seluruh warning akan menjadi error.
  3. gcc tidak menghasilkan binary: file /tmp/test_warn_unused2 tidak dibuat karena linking dibatalkan.

-Wsign-compare dan Jebakan Loop #

Perbandingan antara int (signed) dan size_t (unsigned) adalah sumber bug klasik. Variabel loop int i yang dibandingkan dengan sizeof(arr) akan memunculkan warning -Wsign-compare. Tanpa -Wextra, warning ini tidak muncul dan bug tetap diam-diam.

c
/* File: /tmp/test_warn_cmp.c */
#include <stdio.h>
#include <stddef.h>
int main(void) {
    int arr[5] = {0, 1, 2, 3, 4};
    size_t n = sizeof(arr) / sizeof(arr[0]);
    for (int i = 0; i <= n; i++) {  /* bug: <= bukan < */
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}
bash
gcc -std=c11 -Wall -Wextra /tmp/test_warn_cmp.c -o /tmp/test_warn_cmp

Output yang Dihasilkan:

text
/tmp/test_warn_cmp.c: In function 'main':
/tmp/test_warn_cmp.c:6:23: warning: comparison of integer expressions of different signedness: 'int' and 'size_t' {aka 'long unsigned int'} [-Wsign-compare]
    6 |     for (int i = 0; i <= n; i++) {  /* bug: <= bukan < */
      |                       ^~

Pesan warning di atas membongkar dua masalah sekaligus: (1) i bertipe int sedangkan n bertipe size_t yang unsigned, dan (2) jangan gunakan <= untuk batas array karena indeks valid adalah 0..n-1. Perbaikan yang benar adalah mengubah tipe i menjadi size_t dan operator <= menjadi <:

c
for (size_t i = 0; i < n; i++) {
    printf("%d ", arr[i]);
}

-Wformat Menangkap Bug printf #

Salah satu warning paling produktif di grup -Wall adalah -Wformat yang mencocokkan format specifier (%d, %s, %f) dengan tipe argumen yang diberikan. Bug seperti printf("%d", variabel_double) sering lolos review manual karena program tetap berjalan, hanya saja mencetak angka yang salah.

c
/* File: /tmp/test_warn_fmt.c */
#include <stdio.h>
int main(void) {
    double pi = 3.14;
    printf("pi = %d\n", pi);  /* bug: %d untuk double */
    return 0;
}
bash
gcc -std=c11 -Wall -Wextra /tmp/test_warn_fmt.c -o /tmp/test_warn_fmt

Output yang Dihasilkan:

text
/tmp/test_warn_fmt.c: In function 'main':
/tmp/test_warn_fmt.c:4:19: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat=]
    4 |     printf("pi = %d\n", pi);  /* bug: %d untuk double */
      |                  ~^     ~~
      |                   |     |
      |                   int   double
      |                  %f

gcc bahkan mencetak saran %f sebagai pengganti %d — sebuah diff kecil yang bisa langsung kita terapkan.

-Wreturn-type untuk Fungsi yang Tidak Lengkap #

Standar C menyatakan bahwa fungsi non-void harus mengembalikan nilai pada setiap path eksekusi. Bila ada cabang yang lupa return, perilaku program menjadi undefined. -Wreturn-type memunculkan warning untuk kasus ini.

c
/* File: /tmp/test_warn_pedantic.c */
#include <stdio.h>
int fungsi_tanpa_return(int x) {
    if (x > 0) {
        printf("positif\n");
    }
    /* tidak ada return untuk path negatif */
}
int main(void) {
    fungsi_tanpa_return(5);
    return 0;
}
bash
gcc -std=c11 -Wall -Wextra -Wpedantic /tmp/test_warn_pedantic.c -o /tmp/test_warn_pedantic

Output yang Dihasilkan:

text
/tmp/test_warn_pedantic.c: In function 'fungsi_tanpa_return':
/tmp/test_warn_pedantic.c:7:1: warning: control reaches end of non-void function [-Wreturn-type]
    7 | }
      | ^

Pesan control reaches end of non-void function menandakan ada jalur yang mencapai akhir fungsi tanpa mengeksekusi return. Perbaikan adalah menambah return 0; (atau nilai default lain) di akhir fungsi.

Kombinasi Standar Industri #

Makefile atau build script modern umumnya memakai kombinasi flag berikut sebagai standar minimum:

bash
gcc -std=c11 -Wall -Wextra -Wpedantic -Werror -g -O0 -o app app.c

Mari kita uraikan setiap flag pada perintah di atas:

  1. -std=c11: memakai standar C11 (C ISO/IEC 9899:2011). Pilihan c17 atau c23 juga lazim untuk proyek baru.
  2. -Wall -Wextra -Wpedantic: tiga lapis warning utama yang dibahas di atas.
  3. -Werror: semua warning menjadi error, sehingga kompilasi gagal pada warning pertama.
  4. -g: menyertakan simbol debug (DWARF) untuk gdb, addr2line, dan profiler.
  5. -O0: mematikan optimasi agar urutan baris kode sesuai dengan eksekusi mesin (penting untuk debugging).