Saya memiliki beberapa pengalaman belakangan ini dengan fungsi pointer di C.
Jadi melanjutkan dengan tradisi menjawab pertanyaan Anda sendiri, saya memutuskan untuk membuat ringkasan kecil dari dasar-dasar yang sangat, bagi mereka yang membutuhkan menyelam cepat ke subjek.
c
function-pointers
Yuval Adam
sumber
sumber
Jawaban:
Pointer fungsi dalam C
Mari kita mulai dengan fungsi dasar yang akan kita tunjukkan :
Hal pertama, mari kita tentukan pointer ke fungsi yang menerima 2
int
detik dan mengembalikanint
:Sekarang kita dapat dengan aman menunjuk ke fungsi kita:
Sekarang kita memiliki pointer ke fungsi, mari kita gunakan:
Melewati pointer ke fungsi lain pada dasarnya sama:
Kita juga bisa menggunakan fungsi pointer dalam mengembalikan nilai (cobalah untuk menjaga, itu menjadi berantakan):
Tetapi jauh lebih baik menggunakan
typedef
:sumber
pshufb
, itu lambat, jadi implementasi sebelumnya masih lebih cepat. x264 / x265 menggunakan ini secara luas, dan bersifat open source.Pointer fungsi dalam C dapat digunakan untuk melakukan pemrograman berorientasi objek dalam C.
Misalnya, baris berikut ditulis dalam C:
Ya,
->
dan kurangnyanew
operator adalah hadiah mati, tetapi tampaknya menyiratkan bahwa kita sedang mengatur teks beberapaString
kelas menjadi"hello"
.Dengan menggunakan fungsi pointer, adalah mungkin untuk meniru metode dalam C .
Bagaimana ini dicapai?
The
String
kelas sebenarnya adalahstruct
dengan sekelompok pointer fungsi yang bertindak sebagai cara untuk metode simulasi. Berikut ini adalah deklarasi sebagian dariString
kelas:Seperti dapat dilihat, metode
String
kelas sebenarnya adalah pointer fungsi ke fungsi yang dideklarasikan. Dalam menyiapkan instance dariString
,newString
fungsi dipanggil untuk mengatur pointer fungsi ke fungsi masing-masing:Misalnya,
getString
fungsi yang dipanggil dengan memanggilget
metode didefinisikan sebagai berikut:Satu hal yang dapat diperhatikan adalah bahwa tidak ada konsep instance dari objek dan memiliki metode yang sebenarnya merupakan bagian dari objek, sehingga "objek diri" harus diteruskan pada setiap doa. (Dan
internal
itu hanya tersembunyistruct
yang dihilangkan dari daftar kode sebelumnya - ini adalah cara melakukan penyembunyian informasi, tetapi itu tidak relevan dengan pointer fungsi.)Jadi, daripada bisa melakukan
s1->set("hello");
, seseorang harus meneruskan objek untuk melakukan tindakans1->set(s1, "hello")
.Dengan penjelasan minor harus lulus dalam referensi untuk diri sendiri keluar dari jalan, kami akan pindah ke bagian berikutnya, yaitu warisan di C .
Katakanlah kita ingin membuat subkelas dari
String
, katakanlahImmutableString
. Agar string tidak dapat diubah,set
metode ini tidak akan dapat diakses, dengan tetap mempertahankan akses keget
danlength
, dan memaksa "konstruktor" untuk menerimachar*
:Pada dasarnya, untuk semua subclass, metode yang tersedia adalah pointer fungsi sekali lagi. Kali ini, pernyataan untuk
set
metode ini tidak ada, oleh karena itu, tidak dapat disebut dalam aImmutableString
.Adapun implementasi
ImmutableString
, satu-satunya kode yang relevan adalah fungsi "konstruktor", yaitunewImmutableString
:Dalam instantiating
ImmutableString
, fungsi pointer keget
danlength
metode sebenarnya merujuk keString.get
danString.length
metode, dengan pergi melaluibase
variabel yang merupakanString
objek yang disimpan secara internal .Penggunaan pointer fungsi dapat mencapai pewarisan metode dari superclass.
Kita bisa lanjut terus polimorfisme di C .
Jika misalnya kita ingin mengubah perilaku
length
metode untuk mengembalikan0
semua waktu diImmutableString
kelas karena alasan tertentu, semua yang harus dilakukan adalah:length
metode utama .length
metode utama .Menambahkan
length
metode override diImmutableString
dapat dilakukan dengan menambahkanlengthOverrideMethod
:Kemudian, penunjuk fungsi untuk
length
metode dalam konstruktor dihubungkan kelengthOverrideMethod
:Sekarang, daripada memiliki perilaku yang identik untuk
length
metode diImmutableString
kelas sebagaiString
kelas, sekaranglength
metode akan merujuk ke perilaku yang didefinisikan dalamlengthOverrideMethod
fungsi.Saya harus menambahkan penafian bahwa saya masih belajar bagaimana menulis dengan gaya pemrograman berorientasi objek dalam C, jadi mungkin ada poin yang saya tidak jelaskan dengan baik, atau mungkin hanya meleset dalam hal cara terbaik untuk mengimplementasikan OOP di C. Tapi tujuan saya adalah mencoba mengilustrasikan salah satu dari banyak kegunaan pointer fungsi.
Untuk informasi lebih lanjut tentang cara melakukan pemrograman berorientasi objek di C, silakan merujuk ke pertanyaan berikut:
sumber
ClassName_methodName
konvensi penamaan fungsi. Hanya kemudian Anda mendapatkan runtime dan biaya penyimpanan yang sama seperti yang Anda lakukan di C ++ dan Pascal.Panduan untuk dipecat: Cara menyalahgunakan pointer fungsi di GCC pada mesin x86 dengan mengkompilasi kode Anda dengan tangan:
Literal string ini adalah byte dari kode mesin x86 32-bit.
0xC3
adalah instruksi x86ret
.Anda biasanya tidak akan menulis ini dengan tangan, Anda akan menulis dalam bahasa assembly dan kemudian menggunakan assembler suka
nasm
merakitnya menjadi biner datar yang Anda hexdump menjadi string C literal.Mengembalikan nilai saat ini pada register EAX
Tulis fungsi swap
Tulis penghitung for-loop ke 1000, memanggil beberapa fungsi setiap kali
Anda bahkan dapat menulis fungsi rekursif yang dihitung hingga 100
Perhatikan bahwa kompiler menempatkan string literal di
.rodata
bagian (atau.rdata
pada Windows), yang ditautkan sebagai bagian dari segmen teks (bersama dengan kode untuk fungsi).Segmen teks memiliki izin Baca + Exec, jadi casting string literal ke fungsi pointer berfungsi tanpa perlu
mprotect()
atauVirtualProtect()
panggilan sistem seperti yang Anda perlukan untuk memori yang dialokasikan secara dinamis. (Ataugcc -z execstack
menautkan program dengan tumpukan + segmen data + tumpukan dapat dieksekusi, sebagai peretasan cepat.)Untuk membongkar ini, Anda dapat mengkompilasi ini untuk memberi label pada byte, dan menggunakan disassembler.
Kompilasi
gcc -c -m32 foo.c
dan pembongkaran denganobjdump -D -rwC -Mintel
, kita bisa mendapatkan perakitan, dan mengetahui bahwa kode ini melanggar ABI dengan mengalahkan EBX (register panggilan-diawetkan) dan umumnya tidak efisien.Kode mesin ini akan (mungkin) bekerja dalam kode 32-bit pada Windows, Linux, OS X, dan sebagainya: konvensi pemanggilan default pada semua OS tersebut meneruskan argumen pada stack alih-alih lebih efisien dalam register. Tetapi EBX terpelihara dengan baik dalam semua konvensi panggilan biasa, jadi menggunakannya sebagai register awal tanpa menyimpan / mengembalikannya dapat dengan mudah membuat pemanggil macet.
sumber
Salah satu kegunaan favorit saya untuk pointer fungsi adalah iterator yang murah dan mudah -
sumber
int (*cb)(void *arg, ...)
. Nilai pengembalian iterator juga membuat saya berhenti lebih awal (jika bukan nol).Pointer fungsi menjadi mudah untuk dideklarasikan setelah Anda memiliki deklarator dasar:
ID
: ID adalah*D
: pointer D untukD(<parameters>)
: D fungsi taking<
parameter>
kembaliSedangkan D adalah deklarator lain yang dibuat menggunakan aturan yang sama. Pada akhirnya, di suatu tempat, berakhir dengan
ID
(lihat di bawah untuk contoh), yang merupakan nama entitas yang dinyatakan Mari kita coba membangun fungsi dengan mengambil pointer ke fungsi yang tidak mengambil apa pun dan mengembalikan int, dan mengembalikan sebuah pointer ke fungsi mengambil char dan mengembalikan int. Dengan tipe-def seperti iniSeperti yang Anda lihat, sangat mudah untuk membuatnya menggunakan typedefs. Tanpa typedef, tidak sulit juga dengan aturan deklarator di atas, diterapkan secara konsisten. Seperti yang Anda lihat saya melewatkan bagian penunjuk menunjuk ke, dan hal fungsi kembali. Itulah yang muncul di bagian paling kiri dari deklarasi, dan tidak menarik: itu ditambahkan pada akhirnya jika seseorang sudah membangun deklarator. Ayo lakukan itu. Membangunnya secara konsisten, bertele-tele pertama - menunjukkan struktur menggunakan
[
dan]
:Seperti yang Anda lihat, seseorang dapat mendeskripsikan suatu tipe sepenuhnya dengan menambahkan deklarator satu demi satu. Konstruksi dapat dilakukan dengan dua cara. Salah satunya adalah bottom-up, dimulai dengan hal yang sangat benar (daun) dan bekerja hingga pengidentifikasi. Cara lainnya adalah top-down, mulai dari pengenal, bekerja hingga daun. Saya akan menunjukkan dua cara.
Bawah ke Atas
Konstruksi dimulai dengan benda di kanan: Benda dikembalikan, yang merupakan fungsi pengambilan char. Untuk menjaga agar deklarator tetap berbeda, saya akan beri nomor:
Memasukkan parameter char secara langsung, karena sepele. Menambahkan pointer ke deklarator dengan mengganti
D1
dengan*D2
. Perhatikan bahwa kita harus membungkus tanda kurung*D2
. Itu bisa diketahui dengan melihat prioritas*-operator
operator dan fungsi-panggilan()
. Tanpa tanda kurung, kompiler akan membacanya sebagai*(D2(char p))
. Tapi itu tidak akan menjadi pengganti D1*D2
lagi, tentu saja. Tanda kurung selalu diizinkan di sekitar deklarator. Jadi Anda tidak membuat kesalahan apa pun jika Anda menambahkannya terlalu banyak, sebenarnya.Jenis pengembalian selesai! Sekarang, mari kita ganti
D2
dengan fungsi deklarator fungsi mengambil<parameters>
kembali ,D3(<parameters>)
yang kita sekarang.Perhatikan bahwa tidak ada tanda kurung yang diperlukan, karena kami ingin
D3
menjadi deklarator fungsi dan bukan deklarator pointer kali ini. Hebat, satu-satunya yang tersisa adalah parameter untuk itu. Parameter dilakukan persis sama seperti yang kita lakukan pada tipe pengembalian, hanya denganchar
diganti olehvoid
. Jadi saya akan menyalinnya:Saya sudah diganti
D2
olehID1
, karena kita sudah selesai dengan parameter itu (itu sudah pointer ke fungsi - tidak perlu deklarator lain).ID1
akan menjadi nama parameter. Sekarang, saya katakan di atas pada bagian akhir menambahkan tipe yang diubah oleh semua deklarator - yang muncul di paling kiri dari setiap deklarasi. Untuk fungsi, itu menjadi tipe pengembalian. Untuk pointer yang menunjuk untuk mengetik dll ... Sangat menarik ketika menuliskan tipe, itu akan muncul dalam urutan yang berlawanan, di bagian paling kanan :) Pokoknya, menggantikannya menghasilkan deklarasi lengkap. Kedua kaliint
tentu saja.Saya telah memanggil pengenal fungsi
ID0
pada contoh itu.Perintahkan ke bawah
Ini dimulai dari pengidentifikasi di paling kiri dalam deskripsi jenis, membungkus deklarator itu saat kami berjalan melalui kanan. Mulailah dengan fungsi yang mengembalikan
<
parameter>
Hal berikutnya dalam deskripsi (setelah "kembali") adalah penunjuk ke . Mari menggabungkannya:
Kemudian hal berikutnya adalah fungsi mengambil
<
parameter>
kembali . Parameternya adalah char sederhana, jadi kami langsung memasukkannya lagi, karena ini benar-benar sepele.Perhatikan tanda kurung kami menambahkan, karena kita lagi mau bahwa
*
mengikat pertama, dan kemudian para(char)
. Jika tidak maka akan membaca fungsi mengambil<
parameter>
kembali fungsi ... . Noes, fungsi yang mengembalikan fungsi bahkan tidak diperbolehkan.Sekarang kita hanya perlu meletakkan
<
parameter>
. Saya akan menunjukkan versi singkat dari deriveration, karena saya pikir Anda sudah memiliki ide untuk melakukannya.Letakkan di
int
depan deklarator seperti yang kami lakukan dengan bottom-up, dan kami selesaiYang menyenangkan
Apakah bottom-up atau top-down lebih baik? Saya sudah terbiasa dengan bottom-up, tetapi beberapa orang mungkin lebih nyaman dengan top-down. Ini masalah selera saya pikir. Secara kebetulan, jika Anda menerapkan semua operator dalam deklarasi itu, Anda akan mendapatkan int:
Itu adalah properti bagus dari deklarasi di C: Deklarasi ini menegaskan bahwa jika operator tersebut digunakan dalam ekspresi menggunakan pengenal, maka itu menghasilkan tipe di paling kiri. Seperti itu juga untuk array.
Semoga Anda menyukai tutorial kecil ini! Sekarang kita dapat menautkan ini ketika orang bertanya-tanya tentang sintaksis deklarasi fungsi yang aneh. Saya mencoba untuk menempatkan internal C sesedikit mungkin. Jangan ragu untuk mengedit / memperbaiki hal-hal di dalamnya.
sumber
Penggunaan lain yang bagus untuk pointer fungsi:
Beralih antar versi tanpa rasa sakit
Mereka sangat berguna untuk digunakan ketika Anda ingin fungsi yang berbeda di waktu yang berbeda, atau fase pengembangan yang berbeda. Misalnya, saya sedang mengembangkan aplikasi pada komputer host yang memiliki konsol, tetapi rilis akhir dari perangkat lunak akan diletakkan pada Avnet ZedBoard (yang memiliki port untuk tampilan dan konsol, tetapi tidak diperlukan / diinginkan untuk rilis final). Jadi selama pengembangan, saya akan gunakan
printf
untuk melihat status dan pesan kesalahan, tetapi ketika saya selesai, saya tidak ingin apapun dicetak. Inilah yang telah saya lakukan:version.h
Dalam
version.c
I akan mendefinisikan 2 fungsi prototipe hadirversion.h
version.c
Perhatikan bagaimana pointer fungsi di prototipe
version.h
sebagaivoid (* zprintf)(const char *, ...);
Ketika direferensikan dalam aplikasi, itu akan mulai mengeksekusi di mana pun itu menunjuk, yang belum didefinisikan.
Dalam
version.c
, perhatikan diboard_init()
fungsi di manazprintf
ditugaskan fungsi unik (yang tanda tangan fungsinya cocok) tergantung pada versi yang didefinisikan dalamversion.h
zprintf = &printf;
zprintf memanggil printf untuk tujuan debuggingatau
zprintf = &noprint;
zprintf hanya kembali dan tidak akan menjalankan kode yang tidak perluMenjalankan kode akan terlihat seperti ini:
mainProg.c
Kode di atas akan digunakan
printf
jika dalam mode debug, atau tidak melakukan apa pun jika dalam mode rilis. Ini jauh lebih mudah daripada melalui seluruh proyek dan mengomentari atau menghapus kode. Semua yang perlu saya lakukan adalah mengubah versiversion.h
dan kode akan melakukan sisanya!sumber
Pointer fungsi biasanya didefinisikan oleh
typedef
, dan digunakan sebagai nilai param & return.Di atas jawaban sudah banyak dijelaskan, saya hanya memberikan contoh lengkap:
sumber
Salah satu kegunaan besar untuk pointer fungsi di C adalah untuk memanggil fungsi yang dipilih saat run-time. Misalnya, pustaka run-time C memiliki dua rutinitas,
qsort
danbsearch
, yang membawa pointer ke fungsi yang dipanggil untuk membandingkan dua item yang sedang diurutkan; ini memungkinkan Anda untuk mengurutkan atau mencari, masing-masing, apa pun, berdasarkan kriteria apa pun yang ingin Anda gunakan.Contoh yang sangat mendasar, jika ada satu fungsi yang dipanggil
print(int x, int y)
yang pada gilirannya mungkin perlu memanggil fungsi (baikadd()
atausub()
, yang dari jenis yang sama) maka apa yang akan kita lakukan, kita akan menambahkan satu argumen penunjuk fungsi keprint()
fungsi seperti yang ditunjukkan di bawah ini :Outputnya adalah:
sumber
Mulai dari fungsi awal memiliki Beberapa Alamat Memori Darimana Mereka mulai menjalankan. Dalam Bahasa Perakitan Mereka Disebut sebagai (panggilan "fungsi alamat memori"). Sekarang kembali ke C Jika fungsi memiliki alamat memori maka mereka dapat dimanipulasi oleh Pointer dalam C. Jadi oleh aturan C
1.Pertama Anda perlu mendeklarasikan pointer ke fungsi 2. Lewati Alamat fungsi yang Diinginkan
**** Catatan-> fungsinya harus dari jenis yang sama ****
Program Sederhana ini akan Menggambarkan Setiap Hal.
Setelah Itu memungkinkan Lihat Bagaimana mesin Memahami Mereka. Lihatlah instruksi mesin dari program di atas dalam arsitektur 32 bit.
Area tanda merah menunjukkan bagaimana alamat dipertukarkan dan disimpan di eax. Maka mereka adalah instruksi panggilan pada eax. eax berisi alamat fungsi yang diinginkan.
sumber
Pointer fungsi adalah variabel yang berisi alamat suatu fungsi. Karena itu adalah variabel pointer meskipun dengan beberapa properti terbatas, Anda dapat menggunakannya cukup banyak seperti Anda akan variabel pointer lainnya dalam struktur data.
Satu-satunya pengecualian yang dapat saya pikirkan adalah memperlakukan pointer fungsi sebagai menunjuk ke sesuatu selain nilai tunggal. Melakukan aritmatika pointer dengan menambah atau mengurangi pointer fungsi atau menambah / mengurangi offset ke pointer fungsi tidak benar-benar bermanfaat karena pointer fungsi hanya menunjuk ke satu hal, titik masuk dari suatu fungsi.
Ukuran variabel pointer fungsi, jumlah byte yang ditempati oleh variabel, dapat bervariasi tergantung pada arsitektur yang mendasarinya, misalnya x32 atau x64 atau apa pun.
Deklarasi untuk variabel penunjuk fungsi perlu menentukan jenis informasi yang sama dengan deklarasi fungsi agar kompiler C melakukan pengecekan seperti biasanya. Jika Anda tidak menentukan daftar parameter dalam deklarasi / definisi pointer fungsi, kompiler C tidak akan dapat memeriksa penggunaan parameter. Ada kasus-kasus ketika kurangnya pemeriksaan ini dapat bermanfaat namun ingatlah bahwa jaring pengaman telah dilepas.
Beberapa contoh:
Dua deklarasi pertama agak mirip yaitu:
func
adalah fungsi yang mengambilint
danchar *
dan mengembalikanint
pFunc
adalah fungsi pointer yang ditugaskan alamat sebuah fungsi yang mengambilint
danchar *
dan kembali sebuahint
Jadi dari atas kita dapat memiliki baris sumber di mana alamat fungsi
func()
ditugaskan ke variabel pointer fungsipFunc
seperti padapFunc = func;
.Perhatikan sintaks yang digunakan dengan deklarasi / definisi pointer fungsi di mana tanda kurung digunakan untuk mengatasi aturan prioritas operator alami.
Beberapa Contoh Penggunaan Yang Berbeda
Beberapa contoh penggunaan pointer fungsi:
Anda dapat menggunakan daftar parameter panjang variabel dalam definisi pointer fungsi.
Atau Anda tidak dapat menentukan daftar parameter sama sekali. Ini bisa bermanfaat tetapi menghilangkan peluang bagi kompiler C untuk melakukan pemeriksaan pada daftar argumen yang disediakan.
Pemain gaya C
Anda dapat menggunakan gips gaya C dengan pointer fungsi. Namun perlu diketahui bahwa kompiler C mungkin longgar tentang pemeriksaan atau memberikan peringatan daripada kesalahan.
Bandingkan Function Pointer dengan Equality
Anda dapat memeriksa bahwa pointer fungsi sama dengan alamat fungsi tertentu menggunakan
if
pernyataan meskipun saya tidak yakin seberapa berguna itu. Operator pembanding lainnya tampaknya memiliki utilitas yang lebih sedikit.Array Pointer Fungsi
Dan jika Anda ingin memiliki array fungsi pointer masing-masing elemen di mana daftar argumen memiliki perbedaan maka Anda dapat mendefinisikan pointer fungsi dengan daftar argumen tidak ditentukan (bukan
void
berarti tidak ada argumen tetapi hanya tidak ditentukan) sesuatu seperti berikut ini meskipun Anda mungkin melihat peringatan dari kompiler C. Ini juga berfungsi untuk parameter penunjuk fungsi ke fungsi:Gaya C
namespace
Menggunakan Globalstruct
dengan Function PointerAnda dapat menggunakan
static
kata kunci untuk menentukan fungsi yang namanya lingkup file dan kemudian menetapkan ini ke variabel global sebagai cara menyediakan sesuatu yang mirip dengannamespace
fungsi C ++.Dalam file header, tentukan struct yang akan menjadi namespace kita bersama dengan variabel global yang menggunakannya.
Kemudian di file sumber C:
Ini kemudian akan digunakan dengan menentukan nama lengkap variabel struct global dan nama anggota untuk mengakses fungsi. The
const
pengubah digunakan pada begitu global yang tidak dapat diubah oleh kecelakaan.Area Aplikasi Pointer Fungsi
Komponen pustaka DLL dapat melakukan sesuatu yang mirip dengan
namespace
pendekatan gaya C di mana antarmuka pustaka tertentu diminta dari metode pabrik di antarmuka pustaka yang mendukung pembuatanstruct
pointer fungsi yang mengandung .. Antarmuka pustaka ini memuat versi DLL yang diminta, membuat struct dengan pointer fungsi yang diperlukan, dan kemudian mengembalikan struct ke pemanggil yang meminta untuk digunakan.dan ini dapat digunakan seperti pada:
Pendekatan yang sama dapat digunakan untuk mendefinisikan lapisan perangkat keras abstrak untuk kode yang menggunakan model tertentu dari perangkat keras yang mendasarinya. Pointer fungsi diisi dengan fungsi khusus perangkat keras oleh pabrik untuk menyediakan fungsionalitas khusus perangkat keras yang mengimplementasikan fungsi yang ditentukan dalam model perangkat keras abstrak. Ini dapat digunakan untuk menyediakan lapisan perangkat keras abstrak yang digunakan oleh perangkat lunak yang memanggil fungsi pabrik untuk mendapatkan antarmuka fungsi perangkat keras tertentu kemudian menggunakan pointer fungsi yang disediakan untuk melakukan tindakan untuk perangkat keras yang mendasarinya tanpa perlu mengetahui detail implementasi tentang target spesifik .
Pointer Fungsi untuk membuat Delegasi, Penangan, dan Callback
Anda dapat menggunakan pointer fungsi sebagai cara untuk mendelegasikan beberapa tugas atau fungsi. Contoh klasik dalam C adalah pointer fungsi delegasi perbandingan yang digunakan dengan fungsi pustaka C Standar
qsort()
danbsearch()
untuk memberikan urutan pemeriksaan untuk menyortir daftar item atau melakukan pencarian biner pada daftar item yang diurutkan. Delegasi fungsi perbandingan menentukan algoritma pemeriksaan yang digunakan dalam pengurutan atau pencarian biner.Penggunaan lain mirip dengan menerapkan algoritma ke wadah Perpustakaan Template C ++ Standar.
Contoh lain adalah dengan kode sumber GUI di mana handler untuk acara tertentu terdaftar dengan menyediakan pointer fungsi yang sebenarnya dipanggil ketika peristiwa itu terjadi. Kerangka kerja Microsoft MFC dengan peta pesannya menggunakan sesuatu yang mirip untuk menangani pesan Windows yang dikirim ke jendela atau utas.
Fungsi asinkron yang memerlukan panggilan balik mirip dengan pengendali event. Pengguna fungsi asinkron memanggil fungsi asinkron untuk memulai beberapa tindakan dan menyediakan penunjuk fungsi yang akan dipanggil fungsi asinkron setelah aksi selesai. Dalam hal ini acara adalah fungsi asinkron yang menyelesaikan tugasnya.
sumber
Karena fungsi pointer sering diketik callback, Anda mungkin ingin melihat jenis panggilan balik aman . Hal yang sama berlaku untuk titik masuk, dll fungsi yang bukan panggilan balik.
C cukup plin-plan dan pemaaf sekaligus :)
sumber