Sebelum compiler menerjemahkan kode C menjadi object file, sebuah tahap yang disebut preprocessor berjalan lebih dulu. Preprocessor melakukan substitusi teks sederhana berdasarkan directive yang diawali tanda #. Pekerjaannya bersifat tekstual: ia memuat file header, mengganti macro, dan memilih blok kode yang akan ikut dikompilasi berdasarkan kondisi tertentu. Pada Chapter ini kita akan mempelajari tiga directive yang paling sering kita jumpai sehari-hari — #include, #define, serta conditional compilation dengan #if/#elif/#else/#endif.
Directive #include #
Directive #include menyuruh preprocessor untuk menyisipkan isi suatu file header ke posisi directive tersebut berada. Ada dua bentuk: #include <nama.h> untuk header pustaka standar, dan #include "nama.h" untuk header buatan kita sendiri. Preprocessor mencari file header di include path yang sudah dikonfigurasi pada compiler.
#include <stdio.h>
int main(void) {
printf("Halo dari stdio\n");
return 0;
}Output yang Dihasilkan:
Halo dari stdioPada contoh di atas, preprocessor membuka file stdio.h milik pustaka standar, menyalin seluruh isinya (deklarasi fungsi printf, tipe FILE, makro EOF, dan lainnya), lalu menyisipkan salinan tersebut menggantikan baris #include <stdio.h>. Setelah substitusi selesai, compiler baru mulai membaca kode dan menemukan deklarasi printf seolah-olah kita menulisnya sendiri.
Directive #define: Object-like dan Function-like Macro #
Directive #define dipakai untuk membuat macro. Macro berbentuk sederhana (object-like macro) hanya mengganti sebuah nama dengan konstanta, sedangkan macro berbentuk fungsi (function-like macro) menerima parameter dan melakukan substitusi yang lebih kompleks.
#include <stdio.h>
#define PI 3.14159
#define LUAS_LINGKARAN(r) (PI * (r) * (r))
int main(void) {
double jari = 5.0;
printf("Luas lingkaran r=%.1f adalah %.4f\n", jari, LUAS_LINGKARAN(jari));
return 0;
}Output yang Dihasilkan:
Luas lingkaran r=5.0 adalah 78.5397Mari kita telaah baris-baris kode di atas:
- Baris 3 (
#define PI 3.14159): object-like macro — preprocessor akan mengganti setiap kemunculanPIdi kode dengan3.14159. - Baris 4 (
#define LUAS_LINGKARAN(r) (PI * (r) * (r))): function-like macro — ketika preprocessor melihatLUAS_LINGKARAN(jari), ia menggantirdenganjari, lalu menggantiPIdengan3.14159, menghasilkan(3.14159 * (jari) * (jari)). - Baris 7 (
printf(...)): pemanggilan macroLUAS_LINGKARAN(jari)sudah disubstitusi menjadi ekspresi matematika yang kemudian dievaluasi saat program dijalankan.
Conditional Compilation dengan #if / #elif / #else / #endif #
Preprocessor juga dapat memilih blok kode mana yang akan dikompilasi berdasarkan kondisi. Mekanisme ini berguna ketika kita ingin membangun program yang sama untuk platform berbeda, atau ketika kita ingin menyalakan fitur debug hanya pada mode pengembangan.
#include <stdio.h>
#define VERSI 2
#if VERSI == 1
#define SALAM "Halo dari versi 1"
#elif VERSI == 2
#define SALAM "Halo dari versi 2"
#else
#define SALAM "Versi tidak dikenal"
#endif
int main(void) {
printf("%s\n", SALAM);
return 0;
}Output yang Dihasilkan:
Halo dari versi 2Pada contoh di atas, preprocessor mengevaluasi konstanta VERSI yang bernilai 2. Hanya blok #elif VERSI == 2 yang ikut dikompilasi; blok #if dan #else dibuang begitu saja sebelum compiler melihatnya. Karena itu, hanya macro SALAM berisi "Halo dari versi 2" yang dikenali oleh compiler.
Konstanta #define untuk Ukuran Array #
Salah satu penggunaan paling lazim dari object-like macro adalah memberi nama pada konstanta numerik yang dipakai berulang, misalnya ukuran array. Pendekatan ini membuat kode lebih mudah dirawat karena perubahan ukuran cukup dilakukan satu kali.
#include <stdio.h>
#define MAKS 5
int main(void) {
int data[MAKS] = {10, 20, 30, 40, 50};
int total = 0;
for (int i = 0; i < MAKS; i++) {
total += data[i];
}
printf("Total: %d\n", total);
printf("Rata-rata: %.2f\n", (double)total / MAKS);
return 0;
}Output yang Dihasilkan:
Total: 150
Rata-rata: 30.00Pada contoh di atas, preprocessor mengganti MAKS dengan 5 di tiga tempat: deklarasi array, kondisi loop, dan ekspresi pembagian rata-rata. Karena penggantian bersifat tekstual, perubahan nilai MAKS cukup dilakukan pada satu baris #define dan otomatis terpakai pada seluruh kode.
Ringkasan Directive Umum #
Berikut adalah ringkasan directive yang sering kita temui:
#include <file.h>— menyisipkan header pustaka standar.#include "file.h"— menyisipkan header buatan sendiri dari direktori proyek.#define NAMA nilai— object-like macro konstanta.#define FUNC(a, b) ...— function-like macro.#if,#elif,#else,#endif— conditional compilation.#ifdef,#ifndef— kompilasi bersyarat berdasarkan definisi macro.#undef NAMA— menghapus definisi macro yang sudah ada.
Pemahaman tentang preprocessor directive menjadi bekal penting ketika kita membaca kode C yang menggunakan pustaka eksternal, karena sebagian besar pustaka memakai macro dan conditional compilation untuk mendukung banyak platform sekaligus.