Keyword inline memberi tahu compiler bahwa suatu fungsi sebaiknya di-inline—disisipkan langsung ke pemanggilnya daripada dipanggil via instruksi call. Tujuannya adalah menghindari overhead pemanggilan fungsi (passing argumen, stack frame, return address) untuk fungsi yang sangat pendek. Inline adalah saran, bukan perintah—compiler bebas mengabaikannya jika menilai inlining tidak menguntungkan. Pada chapter ini kita akan mempraktikkan static inline di header (pola paling aman), inline non-static yang memerlukan definisi extern terpisah, dan perbandingan dengan macro berparameter.

Mekanisme Inline #

Saat compiler melihat fungsi static inline int kuadrat(int x) { return x * x; } dan menemui pemanggilan kuadrat(5), compiler dapat menyisipkan kode 5 * 5 langsung ke tempat pemanggilan—menghindari instruksi call kuadrat dan return. Hasilnya adalah kode yang lebih cepat (tanpa overhead pemanggilan) tetapi biasanya lebih besar (kode fungsi diduplikasi di setiap pemanggilan). Karena itu inline paling cocok untuk fungsi pendek—beberapa baris saja.

Inline juga memungkinkan optimasi lebih lanjut. Compiler dapat melakukan constant folding (mis. kuadrat(5) langsung dihitung saat kompilasi menjadi 25) atau propagasi tipe yang lebih akurat. Pada fungsi yang lebih panjang, inlining dapat justru memperlambat program karena instruction cache menjadi tidak efektif—compiler modern menggunakan heuristic untuk memilih fungsi mana yang sebaiknya di-inline.

Pola static inline di Header #

Pola paling aman dan paling umum: definisikan fungsi sebagai static inline di header. Keyword static memberikan internal linkage—setiap translation unit yang meng-include header mendapatkan salinan fungsi sendiri. Keyword inline memberi tahu compiler bahwa fungsi boleh di-inline. Kombinasi ini menghindari multiple definition karena setiap salinan adalah simbol lokal.

File math_inline.h:

c
#ifndef MATH_INLINE_H
#define MATH_INLINE_H

/* static inline: aman di header, tidak menyebabkan multiple definition */
static inline int kuadrat(int x) {
    return x * x;
}

static inline int maks(int a, int b) {
    return (a > b) ? a : b;
}

static inline double rata_rata(int a, int b) {
    return (a + b) / 2.0;
}

#endif

File main.c:

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

int main(void) {
    int a = 5, b = 7;
    printf("kuadrat(%d) = %d\n", a, kuadrat(a));
    printf("maks(%d, %d) = %d\n", a, b, maks(a, b));
    printf("rata_rata(%d, %d) = %.2f\n", a, b, rata_rata(a, b));
    return 0;
}

Kompilasi dan jalankan:

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

Output yang Dihasilkan:

text
kuadrat(5) = 25
maks(5, 7) = 7
rata_rata(5, 7) = 6.00

Mari kita telaah baris-baris kunci di atas:

  1. Pada math_inline.h baris 6 (static inline int kuadrat(int x) { return x * x; }): definisi fungsi static inline. Keyword static memberi internal linkage sehingga fungsi tidak terlihat dari object file lain (tidak muncul sebagai simbol T di nm). Keyword inline adalah saran ke compiler untuk menyisipkan kode fungsi langsung ke pemanggil.
  2. Pada math_inline.h baris 10 (static inline int maks(int a, int b)): implementasi dengan operator ternary ?:. Fungsi sangat pendek (satu ekspresi) sehingga ideal untuk inline.
  3. Pada math_inline.h baris 14 (static inline double rata_rata(int a, int b)): perhatikan ekspresi (a + b) / 2.0 menggunakan 2.0 (bukan 2) untuk memaksa pembagian floating-point. Jika kita menulis (a + b) / 2, hasilnya adalah pembagian integer yang akan terpotong.
  4. Pada main.c baris 6 (printf("kuadrat(%d) = %d\n", a, kuadrat(a));): pemanggilan kuadrat(a). Compiler dapat menggantinya langsung dengan a * a, menghindari overhead pemanggilan fungsi.
  5. Header boleh di-include berkali-kali: karena fungsi bersifat static, setiap translation unit yang meng-include header mendapatkan salinan lokal fungsi. Linker tidak melihat konflik karena simbol kuadrat di setiap object file bersifat internal (huruf t kecil di nm).

Pola inline Non-Static dengan Definisi Eksternal #

Standar C99 memperkenalkan pola inline non-static yang lebih kompleks. Definisi inline di header tidak menyebabkan multiple definition karena bersifat inline definition—tetapi compiler tidak wajib menyediakan simbol eksternal untuk fungsi. Untuk memungkinkan alamat fungsi diambil (mis. untuk function pointer), kita perlu mendeklarasikan extern inline di salah satu file .c untuk memaksa definisi eksternal.

File util.h:

c
#ifndef UTIL_H
#define UTIL_H

/* inline tanpa static: definisi boleh di header, tetapi butuh
   deklarasi extern di file .c untuk memberi definisi eksternal */
inline int tambah_inline(int a, int b) {
    return a + b;
}

#endif

File util.c:

c
#include "util.h"

/* Mendefinisikan ulang untuk ekspor simbol ke linker */
extern inline int tambah_inline(int a, int b);

File main.c:

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

int main(void) {
    printf("tambah_inline(3, 4) = %d\n", tambah_inline(3, 4));
    return 0;
}

Kompilasi dan jalankan:

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

Output yang Dihasilkan:

text
tambah_inline(3, 4) = 7

Perhatikan bahwa util.c hanya berisi satu baris extern inline int tambah_inline(int a, int b); tanpa badan fungsi. Deklarasi ini memberitahu compiler untuk menyediakan definisi eksternal fungsi—definisi yang akan dipakai jika alamat fungsi diambil atau jika compiler lain memilih untuk tidak meng-inline. Pola ini lebih kompleks daripada static inline dan jarang dipakai pada proyek modern; kebanyakan kode memilih pola static inline di header untuk kesederhanaan.

Perbandingan dengan Macro Berparameter #

Inline function dan macro berparameter sering dianggap sebagai pilihan alternatif. Namun, keduanya berbeda dalam beberapa aspek penting:

Aspekstatic inline functionMacro berparameter
Pemeriksaan tipeYa, oleh compilerTidak, hanya substitusi teks
Side effect gandaTidak—argumen dievaluasi sekaliYa—argumen dapat dievaluasi berkali-kali
DebuggerBisa step into fungsiSulit—kode tidak terlihat sebagai entitas terpisah
Scope variabelMengikuti aturan CHanya teks, dapat bocor
Tipe generikTidak, tipe tertentuYa, polimorfik ala teks
Ukuran translation unitBergantung pada inline decisionSelalu mengembang

Untuk operasi yang membutuhkan tipe generik (mis. MAX untuk int, double, char), macro berparameter masih relevan. Untuk operasi yang dapat didefinisikan dengan tipe tertentu, static inline lebih aman dan lebih mudah di-debug. Standar C11 juga memperkenalkan _Generic yang memungkinkan kita menulis macro generik yang memilih fungsi static inline berdasarkan tipe—solusi terbaik dari kedua dunia.