คุณจะส่งผ่านฟังก์ชั่นเป็นพารามิเตอร์ใน C ได้อย่างไร


616

ฉันต้องการสร้างฟังก์ชั่นที่ทำหน้าที่ส่งผ่านพารามิเตอร์ในชุดข้อมูล คุณจะส่งผ่านฟังก์ชั่นเป็นพารามิเตอร์ใน C ได้อย่างไร


12
หากคุณกำลังใช้ฟังก์ชั่น / typedefอาร์เรย์เป็นตัวแปรใช้เสมอ
Mooing Duck

3
เราเรียกมันว่าFunction pointer
AminM

ชื่อฟังก์ชันควรเป็น ponter ของฟังก์ชัน คนส่วนใหญ่เรียนรู้ C ครอบคลุม qsort ไม่ช้าก็เร็วซึ่งสิ่งนี้หรือไม่
mckenzm

5
@MooingDuck ฉันไม่เห็นด้วยกับข้อเสนอแนะของคุณและข้อเสนอแนะของคุณไม่มีเหตุผลที่จะสนับสนุนข้อสรุป โดยส่วนตัวฉันชอบที่จะไม่ใช้ typedef บนพอยน์เตอร์ของฟังก์ชั่นและฉันคิดว่ามันทำให้โค้ดอ่านง่ายขึ้น
andrewrk

2
@andrewrk: คุณชอบvoid funcA(void(*funcB)(int))และvoid (*funcA())()ไปtypedef void funcB(); void funcA(funcB)และfuncB funcA()? ฉันไม่เห็นหัวกลับหัว
Mooing Duck

คำตอบ:


747

การประกาศ

ต้นแบบสำหรับฟังก์ชั่นที่รับพารามิเตอร์ฟังก์ชั่นมีลักษณะดังต่อไปนี้:

void func ( void (*f)(int) );

นี่ระบุว่าพารามิเตอร์fจะเป็นตัวชี้ไปยังฟังก์ชันที่มีvoidชนิดส่งคืนและใช้intพารามิเตอร์เดียว ฟังก์ชั่นดังต่อไปนี้ ( print) เป็นตัวอย่างของฟังก์ชั่นที่สามารถส่งผ่านไปfuncเป็นพารามิเตอร์เพราะมันเป็นประเภทที่เหมาะสม:

void print ( int x ) {
  printf("%d\n", x);
}

ฟังก์ชั่นการโทร

เมื่อเรียกใช้ฟังก์ชันที่มีพารามิเตอร์ฟังก์ชันค่าที่ส่งจะต้องเป็นตัวชี้ไปยังฟังก์ชัน ใช้ชื่อฟังก์ชั่น (ไม่มีวงเล็บ) สำหรับสิ่งนี้:

func(print);

จะเรียกfuncผ่านฟังก์ชั่นการพิมพ์ไปยังมัน

ฟังก์ชั่นร่างกาย

เช่นเดียวกับพารามิเตอร์ใด ๆfuncตอนนี้สามารถใช้ชื่อของพารามิเตอร์ในส่วนของฟังก์ชั่นเพื่อเข้าถึงค่าของพารามิเตอร์ สมมติว่าfuncจะใช้ฟังก์ชันที่ส่งผ่านไปยังหมายเลข 0-4 ลองพิจารณาก่อนว่าลูปแบบไหนที่จะเรียกการพิมพ์โดยตรง:

for ( int ctr = 0 ; ctr < 5 ; ctr++ ) {
  print(ctr);
}

เนื่องจากfuncการประกาศพารามิเตอร์ของบอกว่าfเป็นชื่อของตัวชี้ไปยังฟังก์ชั่นที่ต้องการเราจำได้ก่อนว่าถ้าfเป็นตัวชี้แล้ว*fเป็นสิ่งที่fชี้ไปที่ (เช่นฟังก์ชั่นprintในกรณีนี้) ดังนั้นแทนที่การพิมพ์ทุกครั้งในลูปด้านบนด้วย*f:

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

แหล่ง


40
ในตัวอย่างรหัสแรกและสุดท้ายของคุณเครื่องหมาย * จะไม่บังคับ ทั้งการกำหนดพารามิเตอร์ฟังก์ชันและการfเรียกใช้ฟังก์ชันสามารถทำได้fโดยไม่ใช้ * มันอาจเป็นความคิดที่ดีที่จะทำอย่างที่คุณทำเพื่อให้ชัดเจนว่าพารามิเตอร์ f เป็นตัวชี้ฟังก์ชัน แต่มันเจ็บการอ่านค่อนข้างบ่อย
Gauthier

5
ดู [c99, 6.9.1§14] สำหรับตัวอย่าง ทั้งสองถูกต้องแน่นอนฉันแค่อยากพูดถึงทางเลือก
Gauthier

6
จริงๆ? คำตอบที่ติดอันดับสูงสุดไม่ได้อ้างอิงถึงการใช้typedefตัวชี้ฟังก์ชั่นสำหรับการอ้างอิงเดียว? ขออภัยต้องลงคะแนนเสียง
Jonathon Reinhart

3
@ JonathonReinhart สิ่งที่จะได้ประโยชน์จากการประเมิน 'typedef'? รุ่นนี้ดูสะอาดกว่ามากและขาดงบพิเศษเช่นกัน เริ่มต้นที่นี่สวยมาก
Abhinav Gauniyal

3
@ JonathonReinhart เห็นดี; ควรสังเกตอย่างชัดเจนว่าพอยน์เตอร์ typedefs ทำให้งงงวยรหัสและดังนั้นจึงไม่ควรใช้
MM

129

คำถามนี้มีคำตอบสำหรับการกำหนดพอยน์เตอร์ของฟังก์ชั่นอยู่แล้วอย่างไรก็ตามพวกมันอาจยุ่งเหยิงมากโดยเฉพาะถ้าคุณกำลังจะส่งพวกมันไปรอบ ๆ แอปพลิเคชันของคุณ เพื่อหลีกเลี่ยงความไม่พึงประสงค์นี้ฉันขอแนะนำให้คุณพิมพ์ตัวชี้ฟังก์ชั่นเป็นสิ่งที่อ่านง่ายขึ้น ตัวอย่างเช่น.

typedef void (*functiontype)();

ประกาศฟังก์ชั่นที่ส่งกลับเป็นโมฆะและไม่มีข้อโต้แย้ง ในการสร้างฟังก์ชั่นตัวชี้ไปที่ประเภทนี้คุณสามารถทำได้:

void dosomething() { }

functiontype func = &dosomething;
func();

สำหรับฟังก์ชั่นที่คืนค่า int และรับถ่านคุณต้องทำ

typedef int (*functiontype2)(char);

และเพื่อใช้งาน

int dosomethingwithchar(char a) { return 1; }

functiontype2 func2 = &dosomethingwithchar
int result = func2('a');

มีห้องสมุดที่สามารถช่วยเปลี่ยนพอยน์เตอร์ของฟังก์ชั่นให้เป็นประเภทที่อ่านได้ดี ฟังก์ชั่นเพิ่มห้องสมุดเป็นที่ดีและมีมูลค่าความพยายาม!

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

ดีกว่าที่กล่าวไว้ข้างต้น


4
หากคุณต้องการที่จะ "เปลี่ยนตัวชี้ฟังก์ชั่นเป็นประเภท" คุณไม่จำเป็นต้องเพิ่มห้องสมุด ใช้เพียงtypedef; มันง่ายกว่าและไม่ต้องการไลบรารีเพิ่มเติม
wizzwizz4

72

ตั้งแต่ C ++ 11 คุณสามารถใช้ไลบรารีที่ใช้งานได้เพื่อทำสิ่งนี้โดยสังเขปและเป็นเรื่องธรรมดา ไวยากรณ์คือเช่น

std::function<bool (int)>

ที่เป็นประเภทการกลับมาที่นี่ของฟังก์ชั่นหนึ่งที่มีการโต้แย้งอาร์กิวเมนต์แรกเป็นประเภทboolint

ฉันได้รวมโปรแกรมตัวอย่างไว้ด้านล่าง:

// 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);
}

แม้ว่าบางครั้งจะสะดวกกว่าในการใช้ฟังก์ชันเทมเพลต:

// 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
คำถามเกี่ยวกับ C; C ++ ใช้ไม่ได้ที่นี่
Super Cat

16
คำถามสำหรับ C ++ ( stackoverflow.com/questions/6339970/ … ) ถูกอ้างถึงที่นี่ดังนั้นฉันคิดว่าคำตอบนี้มีอยู่
mr_T

8
บางทีคำถามของ C ++ ไม่ควรถูกเรียกมาที่นี่เพราะคำถามนี้คือ C.
Michael Fulton

5
คำถามนี้เกิดขึ้นก่อนเมื่อฉันค้นหา "การเรียกใช้ฟังก์ชัน c ++ เป็นพารามิเตอร์" ดังนั้นคำตอบนี้ให้บริการตามวัตถุประสงค์ที่นี่โดยไม่คำนึงถึง
ufoxDan

25

ผ่านที่อยู่ของฟังก์ชั่นเป็นพารามิเตอร์ไปยังฟังก์ชั่นอื่นที่แสดงด้านล่าง

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

นอกจากนี้เราสามารถส่งผ่านฟังก์ชั่นเป็นพารามิเตอร์โดยใช้ตัวชี้ฟังก์ชั่น

#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
คำตอบที่ดีมากมีทั้งบล็อคของโค้ด (แทนที่จะแบ่งทุกอย่างให้เป็นระเบียบที่เข้าใจไม่ได้) คุณสามารถอธิบายความแตกต่างของเทคนิคทั้งสองได้หรือไม่?
Rafael Eyng

5

ฟังก์ชั่นสามารถ "ผ่าน" เป็นตัวชี้ฟังก์ชั่นตาม ISO C11 6.7.6.3p8: " การประกาศของพารามิเตอร์เป็น '' ฟังก์ชั่นกลับมาประเภท '' จะต้องปรับให้ '' ตัวชี้ไปยังฟังก์ชั่นกลับประเภท ''เช่นใน 6.3 .2.1. " ตัวอย่างเช่นสิ่งนี้:

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

เทียบเท่ากับสิ่งนี้:

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

คุณอ้างถึงเอกสารอะไร ลิงค์ใด ๆ ไปหรือไม่
Rafael Eyng

1
ฉันอ้างอิงจากมาตรฐาน ISO C11
doppelheathen

4

คุณจำเป็นต้องผ่านตัวชี้ฟังก์ชัน ไวยากรณ์ค่อนข้างยุ่งยาก แต่ก็มีประสิทธิภาพมากเมื่อคุณคุ้นเคยกับมัน


-2

มันไม่ได้เป็นฟังก์ชั่น แต่เป็นรหัสชิ้นส่วนที่แปลแล้ว แน่นอนมันไม่ได้ส่งผ่านรหัสเพียงผลลัพธ์ มันจะไม่ทำงานหากถูกส่งไปยังโปรแกรมเลือกจ่ายงานกิจกรรมเพื่อเรียกใช้ในภายหลัง แต่จะแปลรหัสของคุณไว้ในที่เดียวถ้านั่นคือทั้งหมดที่คุณพยายามทำ

#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;
}

คุณแค่เรียกใช้ฟังก์ชัน คุณจะใช้ฟังก์ชันอื่นแทนได้IncMultIntอย่างไร
Tejas Pendse
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.