C语言函数指针的老生常谈

 更新时间:2021年11月24日 11:24:47   作者:青锋杨  
这篇文章主要为大家介绍了vue组件通信的几种方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

函数指针

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

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

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

#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了。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • Opencv透视变换综合实例详解

    Opencv透视变换综合实例详解

    这篇文章主要为大家详细介绍了Opencv透视变换综合实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • 详解C++编程中的虚函数

    详解C++编程中的虚函数

    这篇文章主要介绍了详解C++编程中的虚函数,包括在什么情况下应当声明虚函数的相关讲解,需要的朋友可以参考下
    2015-09-09
  • C语言数据结构哈希表详解

    C语言数据结构哈希表详解

    哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过把关键码映射的位置去寻找存放值的地方,说起来可能感觉有点复杂,我想我举个例子你就会明白了,最典型的的例子就是字典
    2022-02-02
  • C语言对结构体数组按照某项规则进行排序的实现过程探究

    C语言对结构体数组按照某项规则进行排序的实现过程探究

    这篇文章主要介绍了C语言对结构体数组按照某项规则进行排序的实现过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-02-02
  • VisualStudio类文件的管理(类文件的分离)的实现

    VisualStudio类文件的管理(类文件的分离)的实现

    在使用 Visual Studio 开发项目的时候,学会进行“类文件的分离”十分重要,本文主要介绍了VisualStudio类文件的管理(类文件的分离)的实现,感兴趣的可以了解一下
    2024-03-03
  • C++泛型编程基本概念详解

    C++泛型编程基本概念详解

    这一篇介绍一下 C++ 编程中与面向对象并列的另一大分支——泛型编程,这一篇主要介绍函数模板、类模板和成员模板三大部分,需要的朋友可以参考下
    2021-08-08
  • C++深入讲解new与deleted关键字的使用

    C++深入讲解new与deleted关键字的使用

    这篇文章主要介绍了C++中new与deleted关键字的使用,new在动态内存中为对象分配空间并返回一个指向该对象的指针;delete接受一个动态对象的指针, 销毁该对象, 并释放与之关联的内存
    2022-05-05
  • Qt 进度条的实现示例

    Qt 进度条的实现示例

    进度条在很多时候都可以用到,有时我们需要在表格,树状栏中直观显示任务进度或消耗百分比,本文就详细的介绍一下Qt 进度条的使用实例,感兴趣的可以了解一下
    2021-06-06
  • C语言数据结构之二叉树详解

    C语言数据结构之二叉树详解

    二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式。本文将通过示例详细讲解一下二叉树,需要的可以参考一下
    2022-03-03
  • C++的对象特性和友元你真的了解吗

    C++的对象特性和友元你真的了解吗

    这篇文章主要为大家详细介绍了C++的对象特性和友元,使用数据库,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02

最新评论