Directive #include adalah jalur utama kita untuk menyusun program C dari banyak file. Tanpa #include, setiap file .c harus mendefinisikan ulang semua deklarasi fungsi pustaka standar seperti printf atau malloc dari nol—pekerjaan yang tidak realistis. Directive ini memberitahu preprocessor untuk menyisipkan isi file lain ke posisi directive, sehingga compiler dapat melihat deklarasi (atau definisi) dari file tersebut sebagai bagian dari translation unit yang sama. Pada chapter ini kita akan membedakan dua bentuk #include, melihat urutan pencarian file yang dipakai preprocessor, dan mencoba menyertakan header lokal sederhana yang kita tulis sendiri.

Dua Bentuk: Angle Brackets vs Double Quotes #

Bentuk pertama, #include <stdio.h>, memakai angle brackets. Preprocessor mencari file di direktori standar sistem (mis. /usr/include) dan direktori yang ditambahkan via flag -I. Bentuk ini dipakai untuk header pustaka standar C dan header pustaka pihak ketiga yang sudah terpasang.

Bentuk kedua, #include "sapa.h", memakai tanda kutip ganda. Preprocessor pertama-tama mencari file di direktori yang sama dengan file sumber yang sedang diproses. Jika tidak ketemu, preprocessor kembali mencari di direktori-direktori yang sama dengan bentuk angle brackets. Karena itu, tanda kutip ganda cocok untuk header yang kita tulis sendiri di proyek.

Mekanisme pencarian ini ditentukan oleh include search path. Kita bisa memeriksa path default yang dipakai GCC dengan menjalankan gcc -print-search-dirs atau melihat output gcc -E -v -x c /dev/null -o /dev/null yang mencantumkan daftar <...> search starts here. Path-path inilah yang dipakai saat preprocessor menemui #include <...>.

Menyertakan Header Lokal #

Kita akan membuat satu file header sapa.h berisi definisi fungsi sapa(), lalu menyertakannya dari main.c. Pada chapter-chapter berikutnya kita akan memisahkan deklarasi (.h) dan definisi (.c), tetapi untuk sementara kita taruh definisi langsung di header agar contoh tetap ringkas.

File sapa.h:

c
#ifndef SAPA_H
#define SAPA_H

#include <stdio.h>

void sapa(const char *nama) {
    printf("Halo, %s!\n", nama);
}

#endif

File main.c:

c
#include <stdio.h>
#include "sapa.h"

int main(void) {
    sapa("Budi");
    sapa("Siti");
    return 0;
}

Kompilasi dan jalankan:

bash
gcc -std=c11 -Wall -Wextra -o app main.c && ./app

Output yang Dihasilkan:

text
Halo, Budi!
Halo, Siti!

Mari kita telaah baris-baris kode di atas:

  1. Baris 1 (#ifndef SAPA_H): include guard pertama. Jika SAPA_H belum didefinisikan, preprocessor melanjutkan memproses isi header. Pola ini mencegah masalah multiple inclusion yang akan kita bahas khusus di chapter Include Guards.
  2. Baris 2 (#define SAPA_H): setelah lolos pemeriksaan di baris 1, preprocessor mendefinisikan SAPA_H agar includsi kedua diabaikan.
  3. Baris 4 (#include <stdio.h>): header sapa.h sendiri membutuhkan printf dari <stdio.h>. Header pustaka standar boleh di-include berkali-kali karena sudah memiliki include guard internal sendiri.
  4. Baris 6 (void sapa(const char *nama) {): definisi fungsi. Pada chapter berikutnya kita akan memisahkan baris ini menjadi deklarasi (.h) dan definisi (.c).
  5. Baris 9 (#endif): menutup blok #ifndef. Preprocessor mengakhiri blok conditional.
  6. Baris 2 pada main.c (#include "sapa.h"): bentuk tanda kutip. Preprocessor mencari sapa.h di direktori yang sama dengan main.c. Jika ketemu, seluruh isi sapa.h disisipkan menggantikan directive.
  7. Baris 5 (sapa("Budi");): pemanggilan fungsi sapa yang baru dilihat compiler karena disisipkan via #include "sapa.h".

Header yang Hanya Berisi Macro #

Tidak semua header harus berisi deklarasi fungsi. Banyak header berisi kumpulan konstanta dan macro konfigurasi. Pola seperti ini umum dijumpai pada file config.h atau version.h yang dihasilkan sistem build.

File konfig.h:

c
#define NAMA_APLIKASI "Demo Pro"
#define VERSI_MAJOR 1
#define VERSI_MINOR 2

File main.c:

c
#include <stdio.h>
#include "konfig.h"

int main(void) {
    printf("%s v%d.%d\n", NAMA_APLIKASI, VERSI_MAJOR, VERSI_MINOR);
    return 0;
}

Output yang Dihasilkan:

text
Demo Pro v1.2

Pada contoh di atas, preprocessor mengganti NAMA_APLIKASI menjadi "Demo Pro", VERSI_MAJOR menjadi 1, dan VERSI_MINOR menjadi 2 sebelum compiler memproses printf. Memusatkan nilai konstanta di header membuat pembaruan versi cukup dilakukan di satu tempat.