C/C++學習筆記——C提高: 函數指針和遞歸函數

函數指針

函數類型

通過什麼來區分兩個不同的函數?
一個函數在編譯時被分配一個入口地址,這個地址就稱爲函數的指針,函數名代表函數的入口地址。
函數三要素: 名稱、參數、返回值。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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章