IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言(函数指针) -> 正文阅读

[C++知识库]C语言(函数指针)

函数指针

本质上是一个指针,只不过指向函数而已。

编译器在编译期间对函数开辟了一块空间,而这快空间的开始地址,就是它的函数指针?。

下面我们也直接用最直观的程序来了解函数指针:

#if 1
void func()
{
	printf("hello ptr!");
}

int main()
{
	void (*p)();
	p = func;
	p();
}
#endif

在这里我们给出了func()函数,并在其中打印hello ptr! 。在这里,我们定义了一个函数指针p,大家会发现他的定义语句十分的奇特:? void (*p)()? ?,其实与int a,float b……十分的类似,定义嘛,实际上也就是给出类型,给出变量名,在这里void (*p)()? 给出的类型是指向返回值为void? 无参数传入的函数?,在这里我们把这个指针命名为p。

这条程序中,我们直接将与p类型相对应的函数func()的地址func赋值给p,于是我们便能拿着p去做func的事情了。

?

换言之,如果我们需要定义一个指向返回值为double,有一个int型参数a,一个long型参数b,名称为f_ptr的函数指针呢?

那么显然便是:? double (*f_ptr)(int a,long b)

但其实因为我们只需要告诉编译器我们的函数指针指向的是一个什么类型的函数,所以参数名并不是必须的只需告诉他是什么类型即可,所以我们还可以简写为 double (*f_ptr)(int , long)

我们还要注意到一个小细节:

标准规定:函数名,可以认为是其开始地址
所以函数指针p获取函数地址: ? ? ? p = &Max;? ? ==? ? p = Max;
函数指针p怎么调用:? ? ? ? ? ? ? ? ? ? ?(*p)(10,20);? ?==? ? ?p(10,20);

我们同样能够拿上述程序来做个实验:

?函数指针的应用

函数指针在我们程序中最常见的应用其实应该是作为函数的参数。比如,我们可以这样:

int Add(int a, int b)  //加法实现
{
	return a + b;
}

int Sub(int a, int b)  //减法实现
{
	return a - b;
}

int Mul(int a, int b)  //乘法实现
{
	return a * b;
}

int Div(int a, int b)  //除法实现
{
	if (b == 0)
		return -1;
	return a / b;
}

int Computer(int a, int b, int(*p)(int, int))   //模板函数
{
	return p(a, b);
}

int main()
{
	printf("a+b=%d\n", Computer(10, 20, Add));
	printf("a-b=%d\n", Computer(10, 20, Sub));
	printf("a*b=%d\n", Computer(10, 20, Mul));
	printf("a/b=%d\n", Computer(10, 20, Div));

	return 0;
}

在这里,我们做出了一个模板函数computer,在他的参数列表中,我们给出了int (*p)(int,int)的参数,我们调用函数的时候需要给到这个函数一个函数指针,而我们也在模板函数中使用了函数指针来调用具体函数。

我们定义了Add、Sub、Mul、Div四个具体的实现函数,用来实现相应的加减乘除,而computer只需要接受具体的函数指针来决定他需要干什么,这样为程序的可拓展性提供了非常大的帮助,我们不需要在想要增删功能的时候修改主要代码,仅需增删具体的功能函数就行,甚至部分库函数利用函数指针作为参数的优点来为我们提供可自定义的功能,下面也用一个例子来解释。

函数指针作为参数实例(qsort函数)

qsort函数包含在<stdlib.h>库中

qsort函数的原型描述为:
void qsort( void *base, size_t n_elements, size_t el_sizeint ,int (*compar) (void const * , void const * ) )

qsort函数其实是C语言为我们编写好的一个快速排序函数,他通过函数指针为我们开放了一定的自定义功能

各参数解析,base中传入需要排序的数组,n_elements为该数组中元素的个数,el_sizeint为数组各元素的大小,而compar,则是我们今天反复提及的函数指针,在这里可以理解成为数组内各元素的比较规则。

干说可能很难理解,直接上程序:

int compare_int(const void* a,const void* b)  //用于比较两个整型值的具体函数
{
	//将函数传入的参数进行强转
	int _a = *(const int*)a;
	int _b = *(const int*)b;

	//不相等
	if (_a > _b)
		return 1;
	else if (_a < _b)
		return -1;
	//相等
	else
		return 0;
}

int main()
{
	int arr[] = { 1,3,5,7,2,4,6,7,10,9,8 };
	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), compare_int);
	for (int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
		printf("%d ", arr[i]);

	return 0;
}

此程序中,我们定义了自己的比较规则compare_int函数,传入到qsort函数的最后一个参数,其余参数正常传入,排序成功!

??(qsort函数的比较规则默认返回1则第一个参数大于第二个参数,返回-1则反之,返回0则传入的两个参数相等)??

?在这里强转是必须的,因为默认传入的是两个const void*类型,这在程序中是无法进行比较的。

同时,参数的类型也必须都写为const void* 因为在qsort函数声明中明确表示传入的函数指针应为返回值为int,两个参数都为const void*类型,自定义比较规则函数的参数类型若不为const void*则会造成函数类型不匹配的问题。

我们在这里也不妨想想一个字符串的指针数组应该如何写出我们的自定义函数来排序呢?

int compare_string(const void* _str1, const void* _str2)
{
	//强转类型
	const char* str1 = *(const char**)_str1;
	const char* str2 = *(const char**)_str2;

	//利用string.h库中stramp函数(返回值契合qsort函数比较规则)来进行比较
	int tem = strcmp(str1, str2);
	
	return tem;
}

int main()
{
	const char* arr[] = { "abc","dsa","adfw","odc","adsfa","afsd" };

	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(const char*), compare_string);
	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]);i++)
		printf("%s ", arr[i]);

	return 0;
}

可能在这大家疑惑的地方在于为什么需要使用一个二级指针来进行强转,其实在这里可以用我们平时的指针参数传入来理解,比如void func(int* a),我们传入给这个带*参数时实际上传入的是a的地址。

同理我们传入_str的是什么?我们原数组是一个指针数组,每个元素本质上是个一级指针,那么我们每次比较时传入给compare_string函数一个带*的参数,是不是那便是一级指针的地址,即二级指针,加上一个解引用的*变回一级指针便可以赋给str1了。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-24 07:46:15  更:2021-11-24 07:46:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:49:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码