C語言 - 第三十章 | 函式進階議題 - 函式指標
函式指標
程式在執行時,函式本身在記憶體中也佔有一個空間,而函式名稱本身也就是指向該空間位址的參考名稱,當呼叫函式名稱時,程式就會去執行該函式名稱所指向的記憶體空間中之指令。
一個函式型態由傳回值型態與參數列決定,不包括函式名稱,一個函式指標可指向具有相同型態的函式,也就是具有相同傳回值型態和參數列的函式。
簡單的示範,它以函式指標ptr
來取得函式foo()
的位址,使用它來呼叫函式,這與使用foo()
來呼叫函式具有相同的作用,程式以整數方式顯示兩個的記憶體空間是相同的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <stdio.h>
int foo();
int main() { int (*ptr)() = foo;
foo(); ptr();
printf("address of foo:%p\n", foo); printf("address of ptr:%p\n", ptr);
return 0; }
int foo() { puts("function pointer"); return 0; }
|
1 2 3 4 5
| function pointer function pointer address of foo:001813CF address of ptr:001813CF
|
如果函式帶有參數,則函式指標本身的宣告也必須指定相同的參數型態與個數。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <stdio.h>
int foo1(int, int); char foo2(int, char);
int main() { int (*ptr1)(int, int) = foo1; char (*ptr2)(int, char) = foo2;
ptr1(1, 2); ptr2(3, 'c');
printf("address of foo1(int, int): %p\n", ptr1); printf("address of foo2(int, char): %p\n", ptr2);
return 0; }
int foo1(int n1, int n2) { printf("foo1(int, int): %d\t%d\n", n1, n2); return 0; }
char foo2(int n, char c) { printf("foo2(int, char): %d\t%c\n", n, c); return c; }
|
1 2 3 4 5 6
|
foo1(int, int): 1 2 foo2(int, char): 3 c address of foo1(int, int): 000213DE address of foo2(int, char): 000213D9
|
應用的例子,假設要撰寫一個排序sort()
函式,希望排序時可以由大至小,也可以由小至大,比較簡單的作法是在sort()
加上一個額外的參數,可以傳入常數或列舉,例如如果指定1
的話就由大至小,指定0
的話就由小至大,不過,這需要在函式中加上額外的判斷。
為了簡化sort()
的撰寫,可以傳入一個函式位址,函式中就無需額外的判斷。
1 2 3 4 5
| void swap(int*, int*); int ascending(int, int); int descending(int, int); void sort(int*, int, int (*)(int, int));
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include "sort.h"
void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; }
int ascending(int a, int b) { return a > b; }
int descending(int a, int b) { return a < b; }
void sort(int* arr, int length, int (*compare)(int, int)) { int flag = 1; for(int i = 0; i < length-1 && flag == 1; i++) { flag = 0; for(int j = 0; j < length-i-1; j++) { if(compare(arr[j+1], arr[j])) { swap(arr + j + 1, arr + j); flag = 1; } } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <stdio.h> #include "sort.h"
void print_array(int*, int);
int main() { int number1[] = {3, 5, 1, 6, 9}; sort(number1, 5, ascending); printf("大的在前 "); print_array(number1, 5);
int number2[] = {3, 5, 1, 6, 9}; sort(number2, 5, descending); printf("小的在前 "); print_array(number2, 5);
return 0; }
void print_array(int *arr, int len) { for(int i = 0; i < len; i++) { printf("%d ", arr[i]); } putchar('\n'); }
|
1 2 3
| 大的在前 9 6 5 3 1 小的在前 1 3 5 6 9
|
在函式中,不必理會傳入的實際函式,只要呼叫compare()
就可以了,在這個例子中,sort.h
中sort()
的函式指標宣告有些難以閱讀,可以使用typedef
,定義一個比較容易閱讀的名稱。
1 2 3 4 5 6 7
| typedef int (*CMP)(int, int);
void swap(int*, int*); int ascending(int, int); int descending(int, int); void sort(int*, int, CMP);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include "sort.h"
void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; }
int ascending(int a, int b) { return a > b; }
int descending(int a, int b) { return a < b; }
void sort(int* arr, int length, CMP compare) { int flag = 1; for(int i = 0; i < length-1 && flag == 1; i++) { flag = 0; for(int j = 0; j < length-i-1; j++) { if(compare(arr[j+1], arr[j])) { swap(arr + j + 1, arr + j); flag = 1; } } } }
|
重新使用typedef
定義CMP
名稱後,函式比較容易閱讀的多了。
也可以宣告函式指標陣列
1
| int (*compare[10])(int, int);
|
上面這個宣告產生具有10
個元素的陣列,可以儲存10
個sort
函式型態的位址,不過這樣的宣告實在難以閱讀,可以使用typedef
來改進。
1 2
| typedef int (*CMP)(int, int); CMP compare[10];
|
可以看到這次的宣告比較容易閱讀了。
註:以上參考了
函式指標