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:
#ifndef SAPA_H
#define SAPA_H
#include <stdio.h>
void sapa(const char *nama) {
printf("Halo, %s!\n", nama);
}
#endifFile main.c:
#include <stdio.h>
#include "sapa.h"
int main(void) {
sapa("Budi");
sapa("Siti");
return 0;
}Kompilasi dan jalankan:
gcc -std=c11 -Wall -Wextra -o app main.c && ./appOutput yang Dihasilkan:
Halo, Budi!
Halo, Siti!Mari kita telaah baris-baris kode di atas:
- Baris 1 (
#ifndef SAPA_H): include guard pertama. JikaSAPA_Hbelum didefinisikan, preprocessor melanjutkan memproses isi header. Pola ini mencegah masalah multiple inclusion yang akan kita bahas khusus di chapter Include Guards. - Baris 2 (
#define SAPA_H): setelah lolos pemeriksaan di baris 1, preprocessor mendefinisikanSAPA_Hagar includsi kedua diabaikan. - Baris 4 (
#include <stdio.h>): headersapa.hsendiri membutuhkanprintfdari<stdio.h>. Header pustaka standar boleh di-include berkali-kali karena sudah memiliki include guard internal sendiri. - Baris 6 (
void sapa(const char *nama) {): definisi fungsi. Pada chapter berikutnya kita akan memisahkan baris ini menjadi deklarasi (.h) dan definisi (.c). - Baris 9 (
#endif): menutup blok#ifndef. Preprocessor mengakhiri blok conditional. - Baris 2 pada
main.c(#include "sapa.h"): bentuk tanda kutip. Preprocessor mencarisapa.hdi direktori yang sama denganmain.c. Jika ketemu, seluruh isisapa.hdisisipkan menggantikan directive. - Baris 5 (
sapa("Budi");): pemanggilan fungsisapayang 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:
#define NAMA_APLIKASI "Demo Pro"
#define VERSI_MAJOR 1
#define VERSI_MINOR 2File main.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:
Demo Pro v1.2Pada 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.