Semua alat pada chapter sebelumnya menemukan bug dengan menjalankan program — debugger menghentikan eksekusi, sanitizer mengawasi eksekusi. Static analysis memilih jalur lain: memeriksa program secara murni dengan membaca kodenya, menelusuri semua jalur eksekusi yang mungkin terjadi tanpa menjalankan satu baris pun. Konsekuensi positifnya menarik: bug pada jalur yang jarang tersentuh test tetap bisa ditemukan, karena analisator memang tidak bergantung pada input nyata. g++ 14.2 membawa analisatornya sendiri (-fanalyzer) tanpa instalasi tambahan, dan chapter ini mengujinya langsung pada kode yang memuat bug tersembunyi.
Statis vs Dinamis: Dua Sisi Pemeriksaan #
Kedua keluarga alat menjawab pertanyaan yang berbeda, dan proyek sehat memakai keduanya:
| Aspek | Static analysis | Dynamic (sanitizer) |
|---|---|---|
| Cara kerja | menelusuri kode & semua jalur yang mungkin | mengawasi eksekusi yang benar-benar terjadi |
| Menemukan bug di jalur tak teruji | bisa | tidak |
| Presisi | bisa salah duga (false positive) | nyaris tanpa false positive |
| Overhead | hanya lebih lambat saat kompilasi | program jadi lebih lambat & rakus memori |
| Kapan | tiap commit, murah | tiap test run di dev/CI |
Pembagian kerjanya khas: static analysis murah sehingga dijalankan pada setiap commit, menangkap yang mudah terlihat dari struktur kode; sanitizer lebih presisi tetapi menuntut kode dijalankan, sehingga menangkap yang lolos dari analisis statis. Keduanya saling mengisi, bukan pesaing.
-fanalyzer pada Kode yang “Tampak Baik” #
Perhatikan program ini dari kacamata compiler biasa: ia lolos -Wall -Wextra tanpa satu pun peringatan, berjalan mulus, dan mencetak hasil yang benar:
// analisis.cpp — dari sudut pandang compiler biasa: kode ini "baik-baik saja".
#include <cstdlib>
#include <iostream>
void bacaKonfigurasi() {
char* buffer = static_cast<char*>(std::malloc(64));
if (buffer != nullptr) {
buffer[0] = 'O';
}
// lupa std::free(buffer) — hanya static analysis yang melihatnya
}
int main() {
bacaKonfigurasi();
std::cout << "konfigurasi dimuat\n";
return 0;
}Output yang Dihasilkan:
konfigurasi dimuatKompilasi dan jalankan biasa — tidak ada gejala apa pun; memori 64 byte bocor diam-diam, tepat seperti kasus memory leak yang dibahas pada Section Manajemen Memori. Sekarang jalankan analisator g++ pada file yang sama:
g++ -std=c++23 -Wall -Wextra -fanalyzer analisis.cpp -o analisisLaporan berikut adalah output nyata dari g++ 14.2 di sandbox penulis (analisis statis bersifat deterministik — jalankan berapa kali pun, hasilnya sama):
analisis.cpp: In function ‘void bacaKonfigurasi()’:
analisis.cpp:12:1: warning: leak of ‘buffer’ [CWE-401] [-Wanalyzer-malloc-leak]
12 | }
| ^
‘void bacaKonfigurasi()’: events 1-5
|
| 7 | char* buffer = static_cast<char*>(std::malloc(64));
| | ~~~~~~~~~~~^~~~
| | |
| | (1) allocated here
| 8 | if (buffer != nullptr) {
| | ~~
| | |
| | (2) assuming ‘buffer’ is non-NULL
| | (3) following ‘true’ branch (when ‘buffer’ is non-NULL)...
| 9 | buffer[0] = 'O';
| | ~~~~~~~~~~~~~~~
| | |
| | (4) ...to here
|......
| 12 | }
| | ~
| | |
| | (5) ‘buffer’ leaks here; was allocated at (1)
|Tiga hal yang patut dicermati dari laporan itu:
- Kesimpulannya di baris teratas:
analisis.cpp:12:1: warning: leak of 'buffer' [CWE-401] [-Wanalyzer-malloc-leak]— analisis statis langsung memberi jenis bug dan bahkan nomor CWE (katalog kerentanan umum), berguna saat menulis issue. - Laporan menjelaskan alur, bukan sekadar baris: event (1) alokasi di baris 7, (2)-(3) cabang
ifdianggap diambil, (4) pencatatan tulisan kebuffer[0], lalu (5) keluar function tanpafree— “buffer leaks here; was allocated at (1)”. Kita dibawa menelusuri jalur eksekusi yang bocor. - Ia membaca antar statement: analisator menarik kesimpulan bahwa tidak ada jalur yang memanggil
free, bukan melaporkan “mungkin lupa” untuk setiap pointer — itulah sebabnya laporan ini bisa dipercaya tanpa diverifikasi manual.
Cara Kerja Singkat dan Cara Memakainya #
-fanalyzer membangun state graph: kumpulan keadaan program yang mungkin, lalu mencari keadaan buruk (pointer yang bocor, di-dereference saat null, double free, pemakaian setelah free) yang dapat dicapai dari keadaan awal. Karena ia menelusuri kombinasi, kompiler menjadi jauh lebih lambat — jalankan pada file yang berubah, bukan pada seluruh proyek di setiap pengetikan. Praktik yang sehat:
- Lokal:
-fanalyzerpada file yang baru saja Anda ubah, sebelum commit. - CI: satu job dengan
-fanalyzeruntuk seluruh proyek; waktu kompilasi panjangnya masih wajar untuk job terjadwal. - Toleransi false positive: bila satu peringatan terbukti salah duga, redam secara terarah dengan
-Wno-analyzer-...atau anotasi, alih-alih mematikan analisator.
cppcheck: Analisator Mandiri yang Populer #
Selain yang tertanam di compiler, ada analisator mandiri lintas compiler — yang paling banyak dipakai di proyek C++ adalah cppcheck. Ia fokus pada bug dan gaya yang berbahaya, dengan kecepatan tinggi dan false positive yang rendah. Tool ini tidak terpasang di lingkungan sandbox penulis, sehingga tampilan berikut adalah ilustrasi formatnya (pasang dengan manajer paket distro Anda, misalnya apt install cppcheck, lalu jalankan seperti di bawah):
$ cppcheck --enable=warning analisis.cpp
Checking analisis.cpp ...
analisis.cpp:12:1: error: Memory leak: buffer [memleak]
}
^
nofile:0:0: note: Cppcheck cannot find all the include files (use --check-config for details) [missingIncludeSystem]Format laporannya serupa compiler — berkas:baris:kolom: tingkat: pesan [id] — sehingga mudah diparsing oleh editor dan pipeline. Dua id yang sering muncul di kode pemula: memleak (seperti di atas) dan unusedFunction. Pola kerjanya persis -fanalyzer: tidak ada program yang dijalankan, hanya kode yang dibaca.
Analisator statis menemukan masalah pada cara menulis kode. Ada kelas pemeriksaan lain yang lebih terprogram lagi: membandingkan gaya penulisan terhadap konfigurasi proyek, dari lebar indentasi sampai urutan include. Itu wilayah clang-format — topik chapter berikutnya.
Referensi: GCC — Static Analyzer Options .