#pragma once adalah alternatif modern terhadap pola include guard #ifndef/#define/#endif. Cukup satu baris di puncak header, dan compiler menjamin file tersebut hanya diproses sekali per translation unit, tanpa perlu memikirkan nama token guard. Directive ini didukung oleh GCC, Clang, MSVC, dan mayoritas compiler modern—meskipun secara teknis bukan bagian dari standar ISO C. Pada chapter ini kita akan membandingkan #pragma once dengan include guard klasik, melihat contoh praktis, dan membahas kapan masing-masing layak dipilih.

Cara Kerja #pragma once #

Mekanisme #pragma once berbeda dengan include guard. Pada include guard, preprocessor memutuskan inklusi berdasarkan token macro (MATH_UTILS_H sudah didefinisikan atau belum). Pada #pragma once, compiler menjaga daftar file (berdasarkan inode di sistem POSIX atau canonical path) yang sudah di-include; jika file yang sama diminta lagi, compiler langsung melewati seluruh file tanpa membacanya. Karena itu #pragma once bisa sedikit lebih cepat untuk header besar dan tidak rentan terhadap tabrakan nama token.

c
#pragma once

#define PI 3.14159265

int kuadrat(int x) {
    return x * x;
}

Header di atas menggunakan #pragma once di baris pertama. Tidak ada #ifndef, #define, atau #endif. Hal ini membuat header lebih bersih dan lebih mudah dibaca—terutama untuk file pendek.

Demonstrasi Multi-Inclusion #

Mari kita ulangi demonstrasi include guard dari chapter sebelumnya, tetapi kali ini memakai #pragma once.

File math_utils.h:

c
#pragma once

#define PI 3.14159265

int kuadrat(int x) {
    return x * x;
}

File util.h:

c
#pragma once

#include "math_utils.h"

double luas_lingkaran(double r) {
    return PI * r * r;
}

File main.c:

c
#include <stdio.h>
#include "math_utils.h"
#include "util.h"
#include "math_utils.h"

int main(void) {
    printf("kuadrat(6) = %d\n", kuadrat(6));
    printf("luas_lingkaran(3.0) = %.4f\n", luas_lingkaran(3.0));
    return 0;
}

Kompilasi dan jalankan:

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

Output yang Dihasilkan:

text
kuadrat(6) = 36
luas_lingkaran(3.0) = 28.2743

Tiga kali inclusion math_utils.h (dua eksplisit di main.c, satu tidak langsung via util.h) sama sekali tidak menyebabkan error. Perilaku identik dengan include guard klasik, tetapi dengan jauh lebih sedikit baris kode di header.

Perbandingan dengan Include Guard Klasik #

Aspek-aspek berikut membedakan keduanya:

AspekInclude Guard #ifndef#pragma once
Standar ISO CYa (sejak C89)Tidak, tetapi didukung GCC/Clang/MSVC
Baris kode di header3 baris (ifndef/define/endif)1 baris
Risiko tabrakan tokenAda—token bisa tabrakan antar headerTidak ada
Mekanisme deteksiToken macroIdentitas file (inode/path)
Symlink / hard linkTidak terpengaruhBerpotensi keliru (file dianggap berbeda)
Portabilitas compiler lamaSangat baikTergantung compiler

Mari kita telaah implikasi masing-masing:

  1. Standar ISO C: include guard klasik dijamin ada di setiap compiler yang mematuhi standar C (sejak C89). #pragma once tidak standar, tetapi pada praktiknya didukung GCC, Clang, MSVC, Intel, dan IBM XL—yang mencakup hampir seluruh compiler yang akan kita pakai.
  2. Risiko tabrakan token: pada proyek besar, dua header berbeda bisa tidak sengaja memilih token guard yang sama (mis. UTILS_H). Akibatnya, salah satu header diam-diam dilewati. #pragma once menghilangkan risiko ini sepenuhnya.
  3. Symlink dan hard link: #pragma once mengidentifikasi file via inode atau path canonical. Jika sebuah header diakses melalui dua path yang berbeda (mis. via symlink), compiler bisa menganggapnya dua file berbeda. Include guard klasik tidak terpengaruh karena hanya melihat token.
  4. Kecepatan kompilasi: untuk header yang sangat besar (ribuan baris), #pragma once bisa lebih cepat karena compiler membuka file sekali dan langsung melewati pada inclusion berikutnya. Include guard tetap membuka file, lalu melewati setelah #ifndef pertama gagal.

Praktik dan Rekomendasi #

Banyak proyek open-source modern (LLVM, Chromium, Folly) memakai #pragma once sebagai standar. Namun, jika kita membangun pustaka yang harus tetap kompatibel dengan compiler lama atau embedded khusus, include guard klasik tetap menjadi pilihan paling aman. Beberapa proyek juga memakai keduanya—#pragma once di puncak dan include guard sebagai cadangan:

c
#pragma once
#ifndef MATH_UTILS_H
#define MATH_UTILS_H

/* isi header */

#endif /* MATH_UTILS_H */

Pola ini memberikan perlindungan ganda: jika compiler mendukung #pragma once, keduanya menjadi no-op; jika tidak, include guard klasik mengambil alih.