函數指針
函數類型
通過什麼來區分兩個不同的函數?
一個函數在編譯時被分配一個入口地址,這個地址就稱爲函數的指針,函數名代表函數的入口地址。
函數三要素: 名稱、參數、返回值。C語言中的函數有自己特定的類型。
c語言中通過typedef爲函數類型重命名:
typedef int f(int, int); // f 爲函數類型
typedef void p(int); // p 爲函數類型
這一點和數組一樣,因此我們可以用一個指針變量來存放這個入口地址,然後通過該指針變量調用函數。
注意:通過函數類型定義的變量是不能夠直接執行,因爲沒有函數體。只能通過類型定義一個函數指針指向某一個具體函數,才能調用。
typedef int(p)(int, int);
void my_func(int a,int b){
printf("%d %d\n",a,b);
}
void test(){
p p1;
//p1(10,20); //錯誤,不能直接調用,只描述了函數類型,但是並沒有定義函數體,沒有函數體無法調用
p* p2 = my_func;
p2(10,20); //正確,指向有函數體的函數入口地址
}
函數指針(指向函數的指針)
- 函數指針定義方式(先定義函數類型,根據類型定義指針變量);
- 先定義函數指針類型,根據類型定義指針變量;
- 直接定義函數指針變量;
int my_func(int a,int b){
printf("ret:%d\n", a + b);
return 0;
}
//1. 先定義函數類型,通過類型定義指針
void test01(){
typedef int(FUNC_TYPE)(int, int);
FUNC_TYPE* f = my_func;
//如何調用?
(*f)(10, 20);
f(10, 20);
}
//2. 定義函數指針類型
void test02(){
typedef int(*FUNC_POINTER)(int, int);
FUNC_POINTER f = my_func;
//如何調用?
(*f)(10, 20);
f(10, 20);
}
//3. 直接定義函數指針變量
void test03(){
int(*f)(int, int) = my_func;
//如何調用?
(*f)(10, 20);
f(10, 20);
}
函數指針數組
函數指針數組,每個元素都是函數指針。
void func01(int a){
printf("func01:%d\n",a);
}
void func02(int a){
printf("func02:%d\n", a);
}
void func03(int a){
printf("func03:%d\n", a);
}
void test(){
#if 0
//定義函數指針
void(*func_array[])(int) = { func01, func02, func03 };
#else
void(*func_array[3])(int);
func_array[0] = func01;
func_array[1] = func02;
func_array[2] = func03;
#endif
for (int i = 0; i < 3; i ++){
func_array[i](10 + i);
(*func_array[i])(10 + i);
}
}
函數指針做函數參數(回調函數)
函數參數除了是普通變量,還可以是函數指針變量。
//形參爲普通變量
void fun( int x ){}
//形參爲函數指針變量
void fun( int(*p)(int a) ){}
函數指針變量常見的用途之一是把指針作爲參數傳遞到其他函數,指向函數的指針也可以作爲參數,以實現函數地址的傳遞。
//加法計算器
int plus(int a,int b){
return a + b;
}
//減法計算器
int minus(int a,int b){
return a - b;
}
//計算器
#if 0
int caculator(int a,int b,int(*func)(int,int)){
return func(a, b);
}
#else
typedef int(*FUNC_POINTER)(int, int);
int caculator(int a, int b, FUNC_POINTER func){
return func(a, b);
}
#endif
注意:函數指針和指針函數的區別:
- 函數指針是指向函數的指針;
- 指針函數是返回類型爲指針的函數;
遞歸函數
遞歸函數基本概念
C通過運行時堆棧來支持遞歸函數的實現。遞歸函數就是直接或間接調用自身的函數。
普通函數調用
void funB(int b){
printf("b = %d\n", b);
}
void funA(int a){
funB(a - 1);
printf("a = %d\n", a);
}
int main(void){
funA(2);
printf("main\n");
return 0;
}
函數的調用流程如下:
遞歸函數調用
void fun(int a){
if (a == 1){
printf("a = %d\n", a);
return; //中斷函數很重要
}
fun(a - 1);
printf("a = %d\n", a);
}
int main(void){
fun(2);
printf("main\n");
return 0;
}
函數的調用流程如下:
作業:
遞歸實現給出一個數8793,依次打印千位數字8、百位數字7、十位數字9、個位數字3。
void recursion(int val){
if (val == 0){
return;
}
int ret = val / 10;
recursion(ret);
printf("%d ",val % 10);
}
遞歸實現字符串反轉
int reverse1(char *str){
if (str == NULL)
{
return -1;
}
if (*str == '\0') // 函數遞歸調用結束條件
{
return 0;
}
reverse1(str + 1);
printf("%c", *str);
return 0;
}
char buf[1024] = { 0 }; //全局變量
int reverse2(char *str){
if (str == NULL)
{
return -1;
}
if ( *str == '\0' ) // 函數遞歸調用結束條件
{
return 0;
}
reverse2(str + 1);
strncat(buf, str, 1);
return 0;
}
int reverse3(char *str, char *dst){
if (str == NULL || dst == NULL)
{
return -1;
}
if (*str == '\0') // 函數遞歸調用結束條件
{
return 0;
}
reverse3(str + 1);
strncat(dst, str, 1);
return 0;
}