2009-11-18 35 views
14

Tôi hiểu rằng tôi có thể sử dụng con trỏ cho các chức năng.Con trỏ chức năng được sử dụng để làm gì và tôi sẽ sử dụng chúng như thế nào?

Ai đó có thể giải thích lý do tại sao một người sẽ sử dụng chúng và làm cách nào? Đoạn mã ví dụ ngắn sẽ rất hữu ích cho tôi.

+8

Nhìn vào hàm thư viện chuẩn 'qsort()' và xem điều đó có giúp bạn không. –

+1

http://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work/840669#840669 –

+0

Chúng cũng được sử dụng dưới nắp hệ điều hành rất nhiều .. Giống như tôi đã tạo một hệ điều hành với khung điều khiển bằng cách sử dụng rất nhiều con trỏ hàm .. Chúng rất tiện dụng vì nó giống như các phương thức 'virtual/abstract' ngoại trừ bạn phải kiểm soát nó và quản lý nó .. – Earlz

Trả lời

22

Trường hợp đơn giản giống như sau: Bạn có một loạt các hoạt động (chức năng) theo logic nghiệp vụ của bạn. Bạn có hàm băm làm giảm vấn đề đầu vào đối với một trong các hàm logic nghiệp vụ. Một mã sạch sẽ có một mảng các con trỏ hàm, và chương trình của bạn sẽ suy ra một chỉ mục cho mảng đó từ đầu vào và gọi nó.

Dưới đây là một số mẫu mã:

typedef void (*fn)(void) FNTYPE; 
FNTYPE fn_arr[5]; 

fn_arr[0] = fun1; // fun1 is previously defined 
fn_arr[1] = fun2; 
... 

void callMyFun(string inp) { 
    int idx = decideWhichFun(inp); // returns an int between 0 and 4 
    fn_arr[idx](); 
} 

Nhưng tất nhiên, callbacks là việc sử dụng phổ biến nhất. Mã mẫu bên dưới:

void doLengthyOperation(string inp, void (*callback)(string status)) { 
    // do the lengthy task 
    callback("finished"); 
} 

void fnAfterLengthyTask(string status) { 
    cout << status << endl; 
} 

int main() { 
    doLengthyOperation(someinput, fnAfterLengthyTask); 
} 
0

Cách sử dụng khác cho con trỏ là danh sách hoặc mảng lặp lại.

+0

Không dành cho con trỏ hàm. Đọc câu hỏi. –

+1

sử dụng con trỏ hàm trong trình vòng lặp là hoàn toàn hợp lệ có một ví dụ ở đây: http://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work/840669#840669 –

13

Một trường hợp sử dụng khá phổ biến là chức năng gọi lại. Ví dụ, nếu bạn tải một cái gì đó từ một DB bạn có thể thực hiện chức năng tải của bạn để nó báo cáo sự tiến bộ đến một chức năng gọi lại. Điều này có thể được thực hiện với các con trỏ hàm.

+0

Một yêu thích của tôi. Nhưng không chỉ để báo cáo sự tiến bộ - để thực sự trả lại tất cả các hồ sơ! –

+0

Đây là trường hợp sử dụng rất phổ biến. –

5

Gọi lại. Tôi thực hiện một cuộc gọi không đồng bộ đến một đoạn mã và muốn nó cho tôi biết khi nó kết thúc, tôi có thể gửi cho nó một con trỏ hàm để gọi khi nó được thực hiện.

3

Bạn sử dụng con trỏ hàm khi bạn cần cung cấp phương thức gọi lại. Một trong những ví dụ điển hình là đăng ký xử lý tín hiệu - mà hàm sẽ được gọi khi chương trình của bạn được SIGTERM (Ctrl-C)

Dưới đây là một ví dụ:

// The four arithmetic operations ... one of these functions is selected 
// at runtime with a switch or a function pointer 
float Plus (float a, float b) { return a+b; } 
float Minus (float a, float b) { return a-b; } 
float Multiply(float a, float b) { return a*b; } 
float Divide (float a, float b) { return a/b; } 

// Solution with a switch-statement - <opCode> specifies which operation to execute 
void Switch(float a, float b, char opCode) 
{ 
    float result; 

    // execute operation 
    switch(opCode) 
    { 
     case '+' : result = Plus  (a, b); break; 
     case '-' : result = Minus (a, b); break; 
     case '*' : result = Multiply (a, b); break; 
     case '/' : result = Divide (a, b); break; 
    } 

    cout << "Switch: 2+5=" << result << endl;   // display result 
} 

// Solution with a function pointer - <pt2Func> is a function pointer and points to 
// a function which takes two floats and returns a float. The function pointer 
// "specifies" which operation shall be executed. 
void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(float, float)) 
{ 
    float result = pt2Func(a, b); // call using function pointer 

    cout << "Switch replaced by function pointer: 2-5="; // display result 
    cout << result << endl; 
} 

Bạn có thể tìm hiểu thêm về con trỏ chức năng ở đây http://www.newty.de/fpt/index.html

Nếu bạn quen thuộc hơn với ngôn ngữ hướng đối tượng, bạn có thể nghĩ đó là cách C để triển khai strategy design pattern.

+1

+1 nếu ngôn ngữ C có 'cout << endl' (và cả mã C++ của bạn cần phải là' sử dụng không gian tên std; 'để làm việc đó) –

+0

Đâu là đề cập đến' opcode' trong 'Switch_With_Function_Pointer() của bạn 'chức năng? –

+0

@Joy - Trong câu lệnh 'switch()'. –

1

Hãy làm một hàm map -like cho C.

void apply(int *arr, size_t len, int (*func)(int)) 
{ 
    for(size_t i = 0; i < len; i++) 
     arr[i] = func(arr[i]); 
} 

Bằng cách đó, chúng ta có thể biến đổi một chức năng hoạt động trên nguyên để làm việc trên mảng các số nguyên. Chúng tôi cũng có thể thực hiện một phiên bản tương tự:

void apply_enumerated(int *arr, size_t len, int (*func)(size_t, int)) 
{ 
    for(size_t i = 0; i < len; i++) 
     arr[i] = func(i, arr[i]); 
} 

Điều này cũng giống như vậy, nhưng cho phép chức năng của chúng tôi biết yếu tố nào được bật.Chúng ta có thể sử dụng này, ví dụ:

int cube(int i) { return i * i * i } 

void print_array(int *array, size_t len, char *sep) 
{ 
    if(sep == NULL) sep = ", "; 
    printf("%d", *array); 
    for(size_t i = 1; i < len; i++) printf("%s%d", sep, array[i]) 
} 

#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) 

int main(void) 
{ 
    int array[5] = { 1, 2, 3, 4, 5 }; 
    print_array(array, ARRAY_SIZE(array), NULL); 
    apply(array, ARRAY_SIZE(array), cube); 
    print_array(array, ARRAY_SIZE(array), NULL); 
    return 0; 
} 

Đó là mã sẽ in:

1, 2, 3, 4, 5 
1, 8, 27, 64, 125 

Ví dụ liệt kê của chúng tôi:

int mult(size_t i, int j) { return i * j } 

// print_array and ARRAY_SIZE as before 

int main(void) 
{ 
    int array[5] = { 1, 2, 3, 4, 5 }; 
    print_array(array, ARRAY_SIZE(array), NULL); 
    apply_enumerated(array, ARRAY_SIZE(array), mult); 
    print_array(array, ARRAY_SIZE(array), NULL); 
    return 0; 
} 

in này:

1, 2, 3, 4, 5 
0, 2, 6, 12, 20 

Như một ví dụ thế giới thực hơn, thư viện vòng có thể có một hàm áp dụng một hàm hoạt động trên các ký tự đơn cho tất cả các ký tự trong chuỗi. Ví dụ về các chức năng như vậy là chức năng chuẩn của thư viện toupper()tolower() trong ctype.h - chúng tôi có thể sử dụng chức năng string_apply() này để thực hiện chức năng string_toupper() dễ dàng.

3

Tôi ngạc nhiên không ai đề cập đến "máy trạng thái". Các con trỏ hàm là một cách rất phổ biến để triển khai các máy trạng thái cho các nhiệm vụ như phân tích cú pháp. Xem ví dụ: link.

0

Đối với mã, hãy xem câu trả lời của qrdl là state machines tutorials.

Tôi đã sử dụng một biến thể của phương pháp của mình để triển khai thực đơn cho màn hình LCD mà tôi có trên bảng. Rất rất hữu ích, làm cho mã hóa sạch hơn và dễ đọc hơn. Sử dụng các con trỏ hàm làm cho việc mở rộng số lượng, tên và thứ tự các menu rất dễ dàng và ẩn tất cả các chi tiết đó từ hàm gọi hàm cao hơn mà chỉ thấy 'này, tôi viết lên màn hình LCD ở đây'.

Các vấn đề liên quan