Directive #define adalah cara kita memberi nama simbolik pada nilai konstan atau potongan kode yang akan disisipkan oleh preprocessor. Sering disebut macro, fasilitas ini membuat kode lebih mudah dibaca dan dipelihara karena angka-angka ajaib seperti 3.14159 atau 100 dapat diberi nama yang bermakna. Directive pasangannya, #undef, menghapus definisi macro sehingga nama tersebut bebas dipakai ulang—termasuk untuk redefinisi dengan nilai berbeda. Pada chapter ini kita akan melihat cara kerja kedua directive, lingkup macro dari titik definisi sampai titik #undef, dan praktik mengganti konstanta numerik dengan nama simbolik.
Macro Object-Like untuk Konstanta #
Macro object-like adalah bentuk paling sederhana: nama macro diikuti nilai tanpa tanda kurung parameter. Saat preprocessor menjumpai nama macro di kode berikutnya, preprocessor menggantinya dengan nilai yang tercantum pada #define. Karena sekadar substitusi teks, macro dapat berupa angka, string, ekspresi, atau bahkan cuplikan kode C.
#include <stdio.h>
#define PI 3.14
#define NAMA "Demo Undef"
int main(void) {
/* PI masih aktif di sini */
printf("PI = %.2f, NAMA = %s\n", PI, NAMA);
/* Hapus macro PI mulai titik ini */
#undef PI
/* Re-define dengan presisi lebih tinggi */
#define PI 3.14159265
printf("PI (redefinisi) = %.8f\n", PI);
return 0;
}Output yang Dihasilkan:
PI = 3.14, NAMA = Demo Undef
PI (redefinisi) = 3.14159265Perhatikan bahwa PI di baris pertama printf menghasilkan 3.14 sesuai definisi awal, sedangkan PI di baris kedua menghasilkan 3.14159265 sesuai redefinisi setelah #undef. Tanpa #undef, redefinisi macro dengan nilai yang berbeda akan memicu peringatan -Wmacro-redefined dari GCC. #undef membersihkan definisi lama sehingga redefinisi berjalan tanpa warning.
Lingkup Macro: Dari #define Sampai #undef #
Macro bersifat global dalam satu translation unit sejak titik #define ditulis. Macro tidak mengenal block scope seperti variabel; ia aktif dari titik definisi sampai akhir file, atau sampai ditemui #undef yang menghapusnya. Karena itu penempatan #define di tengah fungsi valid secara sintaks—walaupun secara gaya lebih baik menempatkan semua #define di puncak file.
#include <stdio.h>
#define AKTIF 1
int main(void) {
/* Uji apakah macro sudah didefinisikan */
#ifdef AKTIF
printf("AKTIF didefinisikan, nilai = %d\n", AKTIF);
#else
printf("AKTIF tidak didefinisikan\n");
#endif
/* Hapus macro */
#undef AKTIF
#ifdef AKTIF
printf("AKTIF masih ada\n");
#else
printf("AKTIF sudah di-undef, tidak dapat dipakai lagi\n");
#endif
return 0;
}Output yang Dihasilkan:
AKTIF didefinisikan, nilai = 1
AKTIF sudah di-undef, tidak dapat dipakai lagiMari kita telaah baris-baris kode di atas:
- Baris 3 (
#define AKTIF 1): mendefinisikan macroAKTIFdengan nilai1. Mulai titik ini, setiapAKTIFyang muncul di kode akan diganti dengan1. - Baris 7 (
#ifdef AKTIF): directive conditional compilation. JikaAKTIFsudah didefinisikan (yang benar pada titik ini), preprocessor memproses blok di bawahnya hingga#else. - Baris 8 (
printf("AKTIF didefinisikan, nilai = %d\n", AKTIF);): setelah preprocessing,AKTIFdi argumenprintfdiganti dengan1. Baris ini tetap ada pada translation unit hasil preprocessing. - Baris 13 (
#undef AKTIF): menghapus definisiAKTIF. Sejak titik ini, namaAKTIFtidak lagi dikenal preprocessor. - Baris 15 (
#ifdef AKTIF): pemeriksaan kedua. KarenaAKTIFsudah di-undef, kondisi bernilai salah, sehingga preprocessor memilih cabang#elsedan menyisipkan baris 18 ke translation unit. - Baris 18 (
printf("AKTIF sudah di-undef, ...");): satu-satunya baris di blok#ifdefkedua yang bertahan setelah preprocessing.
Praktik: Mengganti Magic Number #
Salah satu manfaat utama #define adalah menghilangkan magic number—angka literal yang muncul di tengah kode tanpa konteks. Memusatkan konstanta di satu tempat membuat perubahan nilai cukup dilakukan sekali, dan kode jauh lebih mudah dibaca.
#include <stdio.h>
#define UKURAN_ARRAY 5
#define BATAS_ATAS 100
#define BATAS_BAWAH 0
int main(void) {
int data[UKURAN_ARRAY] = {10, 25, 50, 75, 90};
for (int i = 0; i < UKURAN_ARRAY; i++) {
if (data[i] >= BATAS_BAWAH && data[i] <= BATAS_ATAS) {
printf("data[%d] = %d (valid)\n", i, data[i]);
}
}
return 0;
}Output yang Dihasilkan:
data[0] = 10 (valid)
data[1] = 25 (valid)
data[2] = 50 (valid)
data[3] = 75 (valid)
data[4] = 90 (valid)Pada contoh di atas, UKURAN_ARRAY, BATAS_ATAS, dan BATAS_BAWAH adalah nama yang menjelaskan diri sendiri. Saat batas perlu diubah dari 100 menjadi 200, kita cukup mengedit satu baris #define BATAS_ATAS 200 dan semua referensi terupdate otomatis oleh preprocessor.