Function pointer adalah konstruksi yang memungkinkan kita menyimpan alamat sebuah fungsi di dalam variabel, lalu memanggil fungsi tersebut secara tidak langsung melalui variabel tersebut. Mekanisme ini menjadi fondasi banyak pola lanjutan di C: callback, dispatch table, plugin system, sebagian besar implementasi generic algorithms seperti qsort dan bsearch. Pada chapter ini kita akan mempelajari cara mendeklarasikan function pointer, menggunakannya sebagai parameter, membungkusnya dengan typedef, dan menyusun array function pointer untuk membentuk dispatch table yang menggantikan rantai if-else panjang.
Dasar Deklarasi Function Pointer #
Sintaks deklarasi function pointer sering terasa membingungkan karena tanda kurung harus membungkus nama pointer agar tidak tertukar dengan deklarasi function yang mengembalikan pointer. Aturan umum: tulis tipe kembalian, lalu (*nama), lalu daftar parameter.
#include <stdio.h>
/* Operasi aritmatika biner sederhana */
int tambah(int a, int b) { return a + b; }
int kurang(int a, int b) { return a - b; }
int kali(int a, int b) { return a * b; }
/* typedef untuk function pointer */
typedef int (*BinOp)(int, int);
int hitung(int x, int y, BinOp op) {
return op(x, y);
}
int main(void) {
BinOp daftar_ops[] = {tambah, kurang, kali};
const char *nama_ops[] = {"tambah", "kurang", "kali"};
int x = 20, y = 5;
for (int i = 0; i < 3; i++) {
int hasil = hitung(x, y, daftar_ops[i]);
printf("%s(%d, %d) = %d\n", nama_ops[i], x, y, hasil);
}
return 0;
}Output yang Dihasilkan:
tambah(20, 5) = 25
kurang(20, 5) = 15
kali(20, 5) = 100Mari kita telaah baris-baris kode di atas:
- Baris 4-6 (
int tambah(int a, int b) { ... }): tiga fungsi biasa dengan signature identik—menerima duaint, mengembalikan satuint. Signature yang sama inilah yang memungkinkan ketiganya disimpan dalam satu array function pointer. - Baris 9 (
typedef int (*BinOp)(int, int);):typedefmemberi namaBinOppada tipe “pointer ke fungsi yang menerima duaintdan mengembalikanint”. Tanpatypedef, signaturehitungakan menjadiint hitung(int x, int y, int (*op)(int, int))yang jauh lebih sulit dibaca. - Baris 11-13 (
int hitung(int x, int y, BinOp op) { return op(x, y); }): parameteropadalah function pointer. Di dalam body,op(x, y)adalah panggilan tidak langsung—ekuivalen dengan(*op)(x, y). - Baris 17 (
BinOp daftar_ops[] = {tambah, kurang, kali};): array berisi tiga function pointer. Nama fungsitambahotomatis di-decay menjadi alamat fungsi tersebut tanpa perlu&. - Baris 22 (
int hasil = hitung(x, y, daftar_ops[i]);): pada setiap iterasi, function pointer berbeda dikirim kehitung. Inilah inti dari pola dispatch tanpaif-else.
Array of Function Pointers sebagai Dispatch Table #
Saat kita memiliki banyak perintah string yang dipetakan ke aksi berbeda, pola if (strcmp(...) == 0) ... else if ... cepat menjadi tidak terbaca. Dispatch table menggantinya dengan array struct berisi nama perintah dan function pointer pelaksananya. Kita tinggal melakukan loop dan memanggil fungsi yang cocok.
#include <stdio.h>
#include <string.h>
typedef enum {
CMD_HELP,
CMD_QUIT,
CMD_TAMBAH,
CMD_KURANG,
CMD_UNKNOWN
} CmdKind;
typedef struct {
const char *nama;
CmdKind kind;
void (*fn)(int, int);
} Command;
static void op_tambah(int a, int b) { printf("hasil = %d\n", a + b); }
static void op_kurang(int a, int b) { printf("hasil = %d\n", a - b); }
static void op_help(int a, int b) {
(void)a; (void)b;
printf("perintah: help, tambah <a> <b>, kurang <a> <b>\n");
}
static void op_quit(int a, int b) {
(void)a; (void)b;
printf("keluar...\n");
}
int main(void) {
Command tabel[] = {
{"help", CMD_HELP, op_help},
{"tambah", CMD_TAMBAH, op_tambah},
{"kurang", CMD_KURANG, op_kurang},
{"quit", CMD_QUIT, op_quit},
};
size_t n = sizeof(tabel) / sizeof(tabel[0]);
/* simulasi input perintah */
const char *perintah[] = {"help", "tambah", "kurang", "tidakada"};
int arg1[] = {0, 7, 10, 0};
int arg2[] = {0, 5, 3, 0};
for (int i = 0; i < 4; i++) {
size_t j;
for (j = 0; j < n; j++) {
if (strcmp(tabel[j].nama, perintah[i]) == 0) {
printf("> %s %d %d\n", perintah[i], arg1[i], arg2[i]);
tabel[j].fn(arg1[i], arg2[i]);
break;
}
}
if (j == n) {
printf("> %s\n(perintah tidak dikenal)\n", perintah[i]);
}
}
return 0;
}Output yang Dihasilkan:
> help 0 0
perintah: help, tambah <a> <b>, kurang <a> <b>
> tambah 7 5
hasil = 12
> kurang 10 3
hasil = 7
> tidakada
(perintah tidak dikenal)Dispatch table di atas memisahkan tiga tanggung jawab yang biasanya bercampur di if-else rantai: pencocokan nama (strcmp), pemilihan handler (tabel[j].fn), dan pemanggilan. Menambah perintah baru hanya memerlukan satu baris baru di array tabel—tidak perlu menyentuh logika utama.
Membandingkan Function Pointer #
Dua function pointer bisa dibandingkan dengan == dan != untuk mengetahui apakah keduanya menunjuk fungsi yang sama. Perbandingan dengan operator lain (<, >) tidak terdefinisi oleh standar C. Berikut ringkasan praktik aman:
- Bandingkan dengan
NULLsebelum memanggil—kebiasaan wajib untuk API yang mengembalikan optional function pointer. - Simpan pasangan
{nama, fn}dalam struct agar mudah dirujuk. - Hindari casting function pointer antar tipe yang berbeda—perilaku tidak terdefinisi kecuali dilakukan cast balik ke tipe aslinya sebelum dipanggil.