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?


12
Jika Anda menggunakan fungsi / array sebagai variabel, SELALU gunakan typedef.
Mooing Duck

3
Kami menyebutnya Function pointer
AminM

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:

void func ( void (*f)(int) ) {
  for ( int ctr = 0 ; ctr < 5 ; ctr++ ) {
    (*f)(ctr);
  }
}

Sumber


40
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.

typedef void (*functiontype)();

Mendeklarasikan fungsi yang mengembalikan batal dan tidak mengambil argumen. Untuk membuat pointer fungsi ke jenis ini sekarang Anda dapat melakukan:

void dosomething() { }

functiontype func = &dosomething;
func();

Untuk fungsi yang mengembalikan int dan mengambil char yang akan Anda lakukan

typedef int (*functiontype2)(char);

dan menggunakannya

int dosomethingwithchar(char a) { return 1; }

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!

boost::function<int (char a)> functiontype2;

jauh lebih baik daripada yang di atas.


4
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:

// g++ test.cpp --std=c++11
#include <functional>

double Combiner(double a, double b, std::function<double (double,double)> func){
  return func(a,b);
}

double Add(double a, double b){
  return a+b;
}

double Mult(double a, double b){
  return a*b;
}

int main(){
  Combiner(12,13,Add);
  Combiner(12,13,Mult);
}

Namun, terkadang, lebih nyaman menggunakan fungsi templat:

// g++ test.cpp --std=c++11

template<class T>
double Combiner(double a, double b, T func){
  return func(a,b);
}

double Add(double a, double b){
  return a+b;
}

double Mult(double a, double b){
  return a*b;
}

int main(){
  Combiner(12,13,Add);
  Combiner(12,13,Mult);
}

43
Pertanyaannya adalah tentang C; C ++ tidak berlaku di sini.
Super Cat

16
Pertanyaan untuk C ++ ( stackoverflow.com/questions/6339970/… ) dirujuk di sini, jadi saya pikir jawaban ini sudah ada.
mr_T

8
Mungkin pertanyaan untuk C ++ tidak boleh dirujuk ke sini karena pertanyaan ini untuk C.
Michael Fulton

5
Pertanyaan ini muncul pertama kali ketika saya mencari "c ++ function call as parameter", jadi jawaban ini melayani tujuannya dengan baik di sini.
ufoxDan

25

Lewati alamat fungsi sebagai parameter ke fungsi lain seperti yang ditunjukkan di bawah ini

#include <stdio.h>

void print();
void execute(void());

int main()
{
    execute(print); // sends address of print
    return 0;
}

void print()
{
    printf("Hello!");
}

void execute(void f()) // receive address of print
{
    f();
}

Kita juga bisa melewatkan fungsi sebagai parameter menggunakan penunjuk fungsi

#include <stdio.h>

void print();
void execute(void (*f)());

int main()
{
    execute(&print); // sends address of print
    return 0;
}

void print()
{
    printf("Hello!");
}

void execute(void (*f)()) // receive address of print
{
    f();
}

1
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:

void foo(int bar(int, int));

setara dengan ini:

void foo(int (*bar)(int, int));

Dari dokumen apa Anda mengutipnya? Ada tautan ke sana?
Rafael Eyng

1
Saya mengutip dari standar ISO C11.
doppelheathen


-2

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>

int IncMultInt(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; } ) );

    return 0;
}

Anda hanya menjalankan fungsi. Bagaimana Anda akan melewati beberapa fungsi lain sebagai pengganti IncMultInt?
Tejas Pendse
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.