指针进阶------>函数和指针(C语言)

我们知道指针的功能很强大。在先前的学习中,我们学到了指针数组和数组指针,今天我们再来介绍一个新的指针---->函数指针,以及函数指针的实际应用的场景。

在正式介绍函数指针之前先补充一个指针数组和数组指针的知识

补充指针数组和数组指针的知识点:

场景一:函数的形参是一级指针(默认用整型举例子,下同):

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//一级指针形参能接受的实参
void test(int* a)
{
	printf("test\n");
}
int main()
{
	int a = 10;
	int* p = &a;
	int arr[] = { 1,2 };
	test(&a);//正确---->整型元素的地址使用整型指针接受
	test(p);//正确--->整型指针传递给整型指针
	test(arr);//正确---->数组名是首元素地址,首元素是整型,等价于传递整型元素地址
	return 0;
}

编译结果如下:

 场景二:形参部分设计为数组指针

#include<stdio.h>
void test(int(*p)[5])
{
	printf("test\n");
}
int main()
{
	int arr[5] = { 0 };
	int arr1[3][5] = { 0 };
	test(&arr);//正确---&arr取出的是数组地址,放到数组指针、
	test(arr1);//正确--->二维数组是数组的数组,数组名是首元素地址,就是一维数组的地址
	return 0;
}

场景三:形参部分设计为二级指针

//二级指针作为形参
#include<stdio.h>
void test(int** ppa)
{
	printf("test\n");
}
int main()
{
	int a = 10;
	int* pa = &a;
	int** ppa = &pa;
	int* arr[10] = { NULL };
	test(&pa);//正确--->一级指针的地址就用二级指针接收
	test(ppa);//正确--->二级指针对应二级指针
	test(arr);//正确--->数组名是数组首元素的地址,首元素是一个一级指针
	return 0;
}

以上三点是对于数组指针和指针数组的补充。下面开始认识函数指针。

1.什么是函数指针

对于函数的调用,系统是要开辟一块空间的,既然要开辟空间,就必然会存在地址,那么有地址就离不开指针!所以说有这么一类指针,它存储的就是函数的地址,下面我们来看一个函数指针

int(*pa)(int,int)---->首先*号和pa先结合,说明pa是一个指针,去掉*号和pa便是指针的类型

为int(int,int)--->说明pa指向的是一个函数,该函数接受两个int类型的实参,返回值的类型也是int。

而函数名就是函数的地址或者是&函数名,两者等价!

接下来,我们看一看怎么使用这个函数指针,就拿这个pa为例,假设我有一个Add函数:

我们先来正常的对Add函数进行调用:

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 10;
	int b = 20;
	int c=Add(a, b);
	printf("%d\n", c);
	
	return 0;
}

毫无疑问输出的结果是30,接下来我们使用函数指针pa进行这样的操作

//函数指针--->Add函数为例
#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 10;
	int b = 20;
	int(*pa)(int, int) = &Add;//pa是一个函数指针,指向了Add这个函数
	//因为pa存储的是Add的地址,那么*pa就相当于了Add,接下来就是正常的函数调用语法
	int c = (*pa)(a, b);//注意()优先级高于*号,所以要把*pa括起来才有解引用的效果
     printf("%d\n", c);
	return 0;
}

运行结果如下:

 说明函数指针同样起到了调用函数的作用。对于函数指针有一个特殊的地方,那就是不需要对指针解引用即可达到函数调用的结果!

int c=pa(a,b);//该段代码和上面解引用版本的代码等效!

 当然,函数指针肯定不是这样用的!因为相比于函数的直接调用还繁琐了不少!下面我们通过一个简易计算器程序的设计来看看函数指针怎么在其中派上大用场!

常规版:

#include<stdio.h>
//简易计算器的实现--->版本1
void menu()
{
	printf("*******************************\n");
	printf("***** 1.Add    2.Sub **********\n");
	printf("***** 3.Mul    4.Div **********\n");
	printf("*****   0.exit      ***********\n");

}
int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x * y;
}
int Div(int x, int y)
{
	if (y == 0)
	{
		return 0;
	}
	else
	{
		return x / y;
	}
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{  
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case 0: printf("退出\n");
			     break;
		case 1: printf("请输入两个操作数: \n");
			    scanf("%d %d", &x, &y);
				ret = Add(x, y);
				printf("%d\n", ret);
				break;
		case 2: printf("请输入两个操作数: \n");
			    scanf("%d %d", &x, &y);
			    ret = Sub(x, y);
			    printf("%d\n", ret);
			    break;
		case 3:
			      printf("请输入两个操作数: \n");
			      scanf("%d %d", &x, &y);
			      ret = Mul(x, y);
			      printf("%d\n", ret);
			     break;
		case 4:  printf("请输入两个操作数: \n");
			     scanf("%d %d", &x, &y);
			     ret = Div(x, y);
			     printf("%d\n", ret);
			     break;
		default:  printf("选择错误\n");
			break;
	    }
	} while (input);
	return 0;
}

运行结果如下:

 对于版本一,我们发现输入在1-4的情况下,首先函数的参数和返回值的类型都相同,其次很多的代码都是相同的,但是却重复写了好多次,这就显得代码十分冗余了,我们希望一份代码能够用多次,所以我们写版本二:

//版本二:使用函数指针
#include<stdio.h>
void menu()
{
	printf("*******************************\n");
	printf("***** 1.Add    2.Sub **********\n");
	printf("***** 3.Mul    4.Div **********\n");
	printf("*****   0.exit      ***********\n");
}
int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x * y;
}
int Div(int x, int y)
{
	if (y == 0)
	{
		return 0;
	}
	else
	{
		return x / y;
	}
}
int main()
{  
	
     int (*pf[5])(int, int)={NULL,Add,Sub,Mul,Div};//pf是一个函数指针数组,也叫做转移表
	 int input = 0;
	 int x = 0;
	 int y = 0;
	 int ret = 0;
	do
	{
		menu();
		printf("请选择: \n");
		scanf("%d", &input);
		if (input == 0)
		{
			printf("退出\n");
			break;
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入操作数 \n");
			scanf("%d %d", &x, &y);
			ret = pf[input](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("选择错误\n");
		}
	} while (input);
	return 0;
}

运行结果如下:

 在版本二里,通过使用函数指针数组存储Add,Sub,Mul,Div的地址,进而通过使用对应的下标对其进行访问取得对应的函数,减少了冗余的代码,提高了程序的可读性!

而函数指针还有一个重要的用途----->回调函数机制!

什么是回调函数?简单地说就是一个函数的形参接受的是一个函数指针,就是我们写的函数并不是直接被我们调用,而是被间接调用。qsort函数就是利用这一函数机制的函数

我们先来看qsort函数的原型:

void qsort( void *base, ---->  起始位置

size_t num, ----->待排序的元素个数

size_t width,---->待排序元素的大小

int ( *compare )(const void *elem1, const void *elem2 ) );

----->函数指针,该函数指针接受两个空类型的指针,返回int

 使用qsort函数的时候,你只需要指定一个你所需要排序的元素的的一个方式,并作为参数传递给qsort函数即可!

排序整型的方法

#include<stdio.h>
#include<stdlib.h> //--->qsort所需的头文件
//qsort排序整型数组和浮点型数组
int cmp_int(const void* e1, const void* e2)//指定的int排序方法
{
	return *(int*)e1 - *(int*)e2;
}
//排序整型
void testi()
{
	int arr[] = { 5,1,3,4,6,7,9 };
	qsort(arr,7, 4, cmp_int);
	for (int i = 0; i < 7; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	testi();
	return 0;
}

排序浮点型的方式:

include<stdio.h>
#include<stdlib.h>
//注意,由于浮点数存储的时候有误差,所以不能通过相减的方式比较大小!
int cmp_float(const void* e1, const void* e2)
{
	if (*(float*)e1 > *(float*)e2)
	{
		return 1;
	}
	else if (*(float*)e1 == *(float*)e2)
	{
		return 0;
	}
	else return -1;
}
void testf()
{
	float arr[] = { 5.0,1.0,3.0,4.0,6.0,7.0,9.0 };
	qsort(arr, 7, sizeof(float), cmp_float);
	for (int i = 0; i < 7; i++)
	{
		printf("%f ", arr[i]);
	}
}
int main()
{
	testf();
	return 0;
}

总结:

1.函数指针---->指向的是函数的地址

2.函数指针的应用1--->相同功能的函数可以在形参的部分抽象成函数指针来接受

3.qsort()函数的使用---->回调函数机制。

最后祝大家新年快乐,共同进步

 


 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值