Bagaimana Anda melewatkan fungsi sebagai parameter dalam C?
616
Saya ingin membuat fungsi yang menjalankan fungsi yang dilewati oleh parameter pada set data. Bagaimana Anda melewatkan fungsi sebagai parameter dalam C?
Nama fungsi harus menjadi ponter untuk fungsi tersebut. Kebanyakan orang yang belajar C membahas qsort cepat atau lambat mana yang melakukan ini?
mckenzm
5
@ MoooDuck Saya tidak setuju dengan saran Anda, dan saran Anda tidak memiliki alasan untuk mendukung kesimpulan. Saya pribadi lebih suka tidak pernah menggunakan typedef pada pointer fungsi, dan saya pikir itu membuat kode lebih jelas dan lebih mudah dibaca.
andrewrk
2
@andrewrk: Anda lebih suka void funcA(void(*funcB)(int))dan void (*funcA())()ke typedef void funcB(); void funcA(funcB)dan funcB funcA()? Saya tidak melihat sisi baiknya.
Mooing Duck
Jawaban:
747
Pernyataan
Prototipe untuk fungsi yang mengambil parameter fungsi terlihat seperti berikut:
void func (void(*f)(int));
Ini menyatakan bahwa parameter fakan menjadi penunjuk ke fungsi yang memiliki voidtipe kembali dan yang mengambil intparameter tunggal . Fungsi berikut ( print) adalah contoh fungsi yang dapat diteruskan ke funcsebagai parameter karena itu adalah tipe yang tepat:
void print (int x ){
printf("%d\n", x);}
Panggilan fungsi
Saat memanggil fungsi dengan parameter fungsi, nilai yang diteruskan harus menjadi penunjuk ke fungsi. Gunakan nama fungsi (tanpa tanda kurung) untuk ini:
func(print);
akan memanggil func, melewati fungsi cetak untuk itu.
Fungsi Tubuh
Seperti halnya parameter apa pun, funcsekarang dapat menggunakan nama parameter di badan fungsi untuk mengakses nilai parameter. Katakanlah yang funcakan menerapkan fungsi itu diteruskan ke angka 0-4. Pertimbangkan, pertama, seperti apa loop itu untuk memanggil cetak secara langsung:
for(int ctr =0; ctr <5; ctr++){
print(ctr);}
Karena funcpernyataan parameter mengatakan itu fadalah nama untuk pointer ke fungsi yang diinginkan, kita ingat pertama bahwa jika fadalah pointer maka *fadalah hal yang fmenunjuk (yaitu fungsi printdalam kasus ini). Akibatnya, ganti saja setiap kemunculan cetakan dalam loop di atas dengan *f:
Dalam contoh kode pertama dan terakhir Anda, * tidak wajib. Definisi parameter fungsi dan fpemanggilan fungsi bisa fsama seperti tanpa *. Mungkin ide yang baik untuk melakukannya seperti yang Anda lakukan, untuk membuatnya jelas bahwa parameter f adalah pointer fungsi. Tapi itu cukup mudah dibaca.
Gauthier
5
Lihat [c99, 6.9.1§14] untuk contohnya. Keduanya benar tentu saja, saya hanya ingin menyebutkan alternatifnya.
Gauthier
6
Betulkah? Jawaban berperingkat teratas tidak membuat referensi tunggal untuk menggunakan typedefpointer fungsi? Maaf, harus memilih-turun.
Jonathon Reinhart
3
@ JonathonReinhart, apa keuntungan dengan 'typedef' apprach? Versi ini terlihat jauh lebih bersih dan tidak memiliki pernyataan tambahan juga. cukup banyak pemula di sini.
Abhinav Gauniyal
3
@ JonathonReinhart terlihat dengan baik; harus secara eksplisit dicatat bahwa pointer typedef mengaburkan kode dan karenanya tidak boleh digunakan.
MM
128
Pertanyaan ini sudah memiliki jawaban untuk mendefinisikan fungsi pointer, tetapi mereka bisa menjadi sangat berantakan, terutama jika Anda akan melewati mereka di aplikasi Anda. Untuk menghindari ketidaknyamanan ini, saya sarankan Anda mengetik fungsi pointer menjadi sesuatu yang lebih mudah dibaca. Sebagai contoh.
typedefvoid(*functiontype)();
Mendeklarasikan fungsi yang mengembalikan batal dan tidak mengambil argumen. Untuk membuat pointer fungsi ke jenis ini sekarang Anda dapat melakukan:
Untuk fungsi yang mengembalikan int dan mengambil char yang akan Anda lakukan
typedefint(*functiontype2)(char);
dan menggunakannya
int dosomethingwithchar(char a){return1;}
functiontype2 func2 =&dosomethingwithchar
int result = func2('a');
Ada perpustakaan yang dapat membantu mengubah pointer fungsi menjadi tipe yang mudah dibaca. The meningkatkan fungsi perpustakaan yang besar dan layak usaha!
Jika Anda ingin "mengubah pointer fungsi menjadi tipe", Anda tidak perlu pustaka boost. Cukup gunakan typedef; lebih sederhana dan tidak memerlukan perpustakaan tambahan.
wizzwizz4
73
Sejak C ++ 11, Anda dapat menggunakan pustaka fungsional untuk melakukan ini secara ringkas dan generik. Sintaksnya adalah, misalnya,
std::function<bool(int)>
di mana booladalah tipe pengembalian di sini dari fungsi satu argumen yang argumen pertamanya adalah tipe int.
Saya telah memasukkan contoh program di bawah ini:
Jawaban yang sangat bagus, dengan seluruh blok kode (bukannya mengiris semuanya menjadi berantakan yang tidak dapat dipahami). Bisakah Anda menguraikan perbedaan kedua teknik?
Rafael Eyng
5
Fungsi dapat "dilewati" sebagai pointer fungsi, sesuai ISO C11 6.7.6.3p8: " Pernyataan parameter sebagai '' tipe pengembalian fungsi '' harus disesuaikan dengan '' pointer ke fungsi tipe pengembalian '' , seperti pada 6.3 .2.1. " Sebagai contoh, ini:
Ini sebenarnya bukan fungsi, tetapi merupakan bagian dari kode yang dilokalkan. Tentu saja tidak lulus kode hanya hasilnya. Ini tidak akan berfungsi jika diteruskan ke dispatcher acara untuk dijalankan di lain waktu (karena hasilnya dihitung sekarang dan tidak ketika acara terjadi). Tapi itu melokalkan kode Anda ke satu tempat jika hanya itu yang Anda coba lakukan.
#include<stdio.h>intIncMultInt(int a,int b){
a++;return a * b;}int main(int argc,char*argv[]){int a =5;int b =7;
printf("%d * %d = %d\n", a, b,IncMultInt(a, b));
b =9;// Create some local code with it's own local variable
printf("%d * %d = %d\n", a, b,({int _a = a+1; _a * b;}));return0;}
typedef
.void funcA(void(*funcB)(int))
danvoid (*funcA())()
ketypedef void funcB(); void funcA(funcB)
danfuncB funcA()
? Saya tidak melihat sisi baiknya.Jawaban:
Pernyataan
Prototipe untuk fungsi yang mengambil parameter fungsi terlihat seperti berikut:
Ini menyatakan bahwa parameter
f
akan menjadi penunjuk ke fungsi yang memilikivoid
tipe kembali dan yang mengambilint
parameter tunggal . Fungsi berikut (print
) adalah contoh fungsi yang dapat diteruskan kefunc
sebagai parameter karena itu adalah tipe yang tepat:Panggilan fungsi
Saat memanggil fungsi dengan parameter fungsi, nilai yang diteruskan harus menjadi penunjuk ke fungsi. Gunakan nama fungsi (tanpa tanda kurung) untuk ini:
akan memanggil
func
, melewati fungsi cetak untuk itu.Fungsi Tubuh
Seperti halnya parameter apa pun,
func
sekarang dapat menggunakan nama parameter di badan fungsi untuk mengakses nilai parameter. Katakanlah yangfunc
akan menerapkan fungsi itu diteruskan ke angka 0-4. Pertimbangkan, pertama, seperti apa loop itu untuk memanggil cetak secara langsung:Karena
func
pernyataan parameter mengatakan ituf
adalah nama untuk pointer ke fungsi yang diinginkan, kita ingat pertama bahwa jikaf
adalah pointer maka*f
adalah hal yangf
menunjuk (yaitu fungsiprint
dalam kasus ini). Akibatnya, ganti saja setiap kemunculan cetakan dalam loop di atas dengan*f
:Sumber
sumber
f
pemanggilan fungsi bisaf
sama seperti tanpa *. Mungkin ide yang baik untuk melakukannya seperti yang Anda lakukan, untuk membuatnya jelas bahwa parameter f adalah pointer fungsi. Tapi itu cukup mudah dibaca.typedef
pointer fungsi? Maaf, harus memilih-turun.Pertanyaan ini sudah memiliki jawaban untuk mendefinisikan fungsi pointer, tetapi mereka bisa menjadi sangat berantakan, terutama jika Anda akan melewati mereka di aplikasi Anda. Untuk menghindari ketidaknyamanan ini, saya sarankan Anda mengetik fungsi pointer menjadi sesuatu yang lebih mudah dibaca. Sebagai contoh.
Mendeklarasikan fungsi yang mengembalikan batal dan tidak mengambil argumen. Untuk membuat pointer fungsi ke jenis ini sekarang Anda dapat melakukan:
Untuk fungsi yang mengembalikan int dan mengambil char yang akan Anda lakukan
dan menggunakannya
Ada perpustakaan yang dapat membantu mengubah pointer fungsi menjadi tipe yang mudah dibaca. The meningkatkan fungsi perpustakaan yang besar dan layak usaha!
jauh lebih baik daripada yang di atas.
sumber
typedef
; lebih sederhana dan tidak memerlukan perpustakaan tambahan.Sejak C ++ 11, Anda dapat menggunakan pustaka fungsional untuk melakukan ini secara ringkas dan generik. Sintaksnya adalah, misalnya,
di mana
bool
adalah tipe pengembalian di sini dari fungsi satu argumen yang argumen pertamanya adalah tipeint
.Saya telah memasukkan contoh program di bawah ini:
Namun, terkadang, lebih nyaman menggunakan fungsi templat:
sumber
Lewati alamat fungsi sebagai parameter ke fungsi lain seperti yang ditunjukkan di bawah ini
Kita juga bisa melewatkan fungsi sebagai parameter menggunakan penunjuk fungsi
sumber
Fungsi dapat "dilewati" sebagai pointer fungsi, sesuai ISO C11 6.7.6.3p8: " Pernyataan parameter sebagai '' tipe pengembalian fungsi '' harus disesuaikan dengan '' pointer ke fungsi tipe pengembalian '' , seperti pada 6.3 .2.1. " Sebagai contoh, ini:
setara dengan ini:
sumber
Anda harus melewati pointer fungsi . Sintaksnya sedikit rumit, tetapi sangat kuat setelah Anda terbiasa dengannya.
sumber
Ini sebenarnya bukan fungsi, tetapi merupakan bagian dari kode yang dilokalkan. Tentu saja tidak lulus kode hanya hasilnya. Ini tidak akan berfungsi jika diteruskan ke dispatcher acara untuk dijalankan di lain waktu (karena hasilnya dihitung sekarang dan tidak ketika acara terjadi). Tapi itu melokalkan kode Anda ke satu tempat jika hanya itu yang Anda coba lakukan.
sumber
IncMultInt
?