Menu

Pointer Fungsi di C: Sintaks, Callback, dan qsort

Fungsi juga punya alamat, dan pointer fungsi menyimpannya. Halaman ini mengurai sintaks deklarasinya, menunjukkan bagaimana typedef membuatnya mudah dibaca, lalu membangun sampai callback, qsort dengan comparator buatan sendiri, dan tabel dispatch.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Kode yang sudah dikompilasi disimpan di memori sama seperti data, jadi sebuah fungsi punya alamat. Pointer fungsi adalah variabel yang menyimpan alamat itu, dan begitu kamu bisa menyimpan sebuah fungsi, kamu bisa mengirimnya sebagai argumen, menyimpan tabelnya, atau memilih di antaranya saat program berjalan.

Kemampuan itulah yang ada di balik pengurutan dengan urutan buatan sendiri, event handler, antarmuka plugin, dan state machine. Ini juga bagian sintaks C yang paling mungkin membuat pembaca berhenti dan memicingkan mata, jadi kita mulai dari situ.

Sintaks Deklarasi

Ambil sebuah fungsi biasa:

int add(int a, int b);

Untuk mendeklarasikan pointer yang bisa menyimpan alamatnya, ganti namanya dengan (*name):

int (*op)(int, int);

Bacalah dari dalam: op adalah pointer, ke sebuah fungsi, yang menerima (int, int), dan mengembalikan int.

Tanda kurung itu bukan hiasan. Tanpanya, * justru terikat ke tipe kembalian:

int (*f)(int);   // pointer to a function taking int, returning int
int *g(int);     // FUNCTION taking int, returning int* - completely different

Berikut contoh lengkap yang berjalan dari awal sampai akhir:

Kedua bentuk pemanggilan berfungsi karena nama fungsi otomatis dikonversi menjadi pointer. Tulis op(10, 3); penulisan (*op)(10, 3) adalah sisa gaya lama.

Satu catatan tentang printf terakhir itu: %p mengharapkan pointer objek, dan pointer fungsi secara formal adalah keluarga yang terpisah, jadi mencetaknya tidak sepenuhnya portabel. Cast itu membungkam compiler di platform di mana hal ini memang bisa dilakukan; kamu jarang sekali perlu mencetaknya.

typedef Membuatnya Mudah Dibaca

Sintaks deklarasinya cepat sekali jadi jelek, dan typedef adalah solusi standarnya:

Bandingkan int apply(BinaryOp op, int x, int y) dengan bentuk mentahnya, int apply(int (*op)(int, int), int x, int y). Artinya sama; yang satu bisa kamu baca sekilas. Lihat typedef untuk aturan umumnya.

Callback: Membiarkan Pemanggil Menentukan Perilaku

Callback adalah fungsi yang kamu serahkan ke fungsi lain agar ia bisa memanggilmu kembali di saat yang tepat. Dengan callback, satu bagian kode mengurus struktur pekerjaannya sementara pemanggil menentukan keputusannya.

Tanpa pointer fungsi, kamu akan menulis count_even, count_positive, dan count_big, yaitu tiga salinan dari loop yang sama. Dengan pointer fungsi, loop-nya ditulis sekali dan pengujiannya yang menjadi parameter.

Manfaat Sebenarnya: qsort

qsort dari library standar adalah contoh pointer fungsi yang benar-benar akan kamu pakai. Fungsi ini mengurutkan array berisi apa saja, dan bisa melakukannya tanpa tahu apa pun tentang elemennya selain ukurannya dan comparator yang kamu berikan.

void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

Comparator menerima dua const void *, yaitu alamat dua elemen, dan mengembalikan angka negatif kalau elemen pertama diurutkan sebelum elemen kedua, nol kalau keduanya setara, dan positif kalau elemen pertama diurutkan sesudahnya.

Ada dua kebiasaan yang perlu dibawa dari comparator itu. Jangan pernah menulis return x - y;: kelihatannya cerdik, tetapi overflow untuk nilai yang besar atau negatif, sehingga tandanya salah dan array-nya tidak terurut dengan benar. Lakukan cast sebelum dereference, bukan sesudahnya: *(const int *)a, karena *a pada void * tidak punya apa-apa untuk dibaca.

Mekanisme yang sama bisa mengurutkan struct berdasarkan field apa pun yang kamu mau:

Pola (q->score > p->score) - (q->score < p->score) mengembalikan tepat -1, 0, atau 1 tanpa pengurangan dan tanpa overflow. Lalu strcmp sudah punya konvensi nilai kembalian yang tepat, jadi comparator untuk string cukup satu baris.

Tabel Pointer Fungsi

Array berisi pointer fungsi mengubah rantai if/else menjadi pencarian di tabel. Begitulah cara interpreter men-dispatch opcode dan cara sistem menu menjalankan perintah.

Menambah operasi sekarang berarti menambah satu baris, bukan mengedit switch. Gabungkan dengan struct dan kamu mendapat bentuk plugin C yang standar: struct berisi pointer fungsi adalah arti "interface" di bahasa ini.

Hal-Hal yang Menjebak

Signature harus cocok persis. Tipe pointer fungsi mencakup tipe parameter dan tipe kembaliannya. Meng-assign fungsi yang tidak cocok adalah pelanggaran batasan, dan memanggil lewat pointer dengan tipe yang salah adalah undefined behavior bahkan saat terlihat "berfungsi". Jangan melakukan cast pada signature comparator demi membungkam peringatan; perbaiki comparator-nya.

Pointer fungsi yang null tetaplah null pointer. Inisialisasi dengan NULL dan periksa sebelum memanggil, persis seperti pointer data:

if (handler != NULL) {
    handler(event);
}

Lihat null pointer untuk alasan pemeriksaan itu layak dilakukan.

Pointer void kehilangan keamanan tipe, dan kamulah sistem tipenya. Di dalam comparator qsort, tidak ada yang mencegahmu melakukan cast ke tipe yang salah. Kalau kamu mengurutkan array double dengan compare_ints, kodenya bisa dikompilasi tanpa masalah tetapi hasilnya ngawur.

Sintaks deklaratornya bisa bersarang. Fungsi yang mengembalikan pointer fungsi ditulis int (*get_op(char c))(int, int);. Saat kamu mendapati dirimu menulis itu, pakai typedef:

typedef int (*BinaryOp)(int, int);
BinaryOp get_op(char c);           // the same thing, readable

Pertanyaan yang sering diajukan

Bagaimana cara mendeklarasikan pointer fungsi di C?

Tulis signature fungsinya dengan (*name) di tempat nama fungsi: int (*op)(int, int); mendeklarasikan op sebagai pointer ke fungsi yang menerima dua int dan mengembalikan int. Tanda kurung di sekitar *op wajib ada: tanpanya, kamu justru mendeklarasikan fungsi yang mengembalikan pointer.

Bagaimana cara memanggil fungsi lewat pointer?

Bisa op(3, 4) atau (*op)(3, 4): keduanya sah dan hasilnya sama, karena nama fungsi otomatis dikonversi menjadi pointer. Kode C modern memakai bentuk op(3, 4) yang biasa.

Apa itu callback di C?

Fungsi yang kamu serahkan ke fungsi lain agar fungsi itu bisa memanggilmu kembali. qsort adalah contoh klasiknya: kamu memberikan comparator, dan qsort memanggilnya setiap kali perlu tahu mana dari dua elemen yang harus didahulukan. Itulah yang membuat satu rutin pengurutan bisa bekerja untuk tipe apa pun.

Apakah perlu & saat mengambil alamat sebuah fungsi?

Tidak. Nama fungsi sudah otomatis dikonversi menjadi pointer ke fungsi itu, jadi op = add; dan op = &add; setara. Sebagian besar kode C tidak menulis &.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai