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

总结

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

相关文章

  • Pthread并发编程之线程基本元素和状态的剖析

    Pthread并发编程之线程基本元素和状态的剖析

    本篇文章主要给大家介绍pthread并发编程当中关于线程的基础概念,并且深入剖析进程的相关属性和设置,以及线程在内存当中的布局形式,帮助大家深刻理解线程
    2022-11-11
  • C语言中回调函数的含义与使用场景详解

    C语言中回调函数的含义与使用场景详解

    这篇文章主要为大家详细介绍了C语言中回调函数的含义与使用场景,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • 详解C++文件读写操作

    详解C++文件读写操作

    这篇文章主要为大家详细介绍了C++文件读写操作,感兴趣的小伙伴们可以参考一下
    2016-03-03
  • C++中cin的返回值问题

    C++中cin的返回值问题

    这篇文章主要介绍了C++中cin的返回值问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • C语言中volatile关键字的作用及说明

    C语言中volatile关键字的作用及说明

    文中主要介绍了C语言中volatile关键字的含义和使用场景,volatile是一个类型修饰符,主要用来修饰被不同线程访问和修改的变量,它的作用是防止编译器对代码进行优化,确保每次直接读取原始内存地址的值
    2024-10-10
  • C语言内存泄露很严重的解决方案

    C语言内存泄露很严重的解决方案

    这篇文章主要介绍了C语言内存泄露很严重的解决方案,预防内存泄漏问题有多种方法,比如加强代码检视、工具检测和内存测试等,下面文章总结内容需要的小伙伴可以参考一下
    2022-05-05
  • 简单实现C++复数计算器

    简单实现C++复数计算器

    这篇文章主要为大家详细介绍了C++简单实现复数计算器的的相关代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • OpenCV中C++函数imread读取图片的问题及解决方法

    OpenCV中C++函数imread读取图片的问题及解决方法

    利用C++函数imread读取图片的时候返回的结果总是空,而利用C函数cvLoadImage时却能读取到图像。怎么回事?今天小编通过本教程给大家简单说明原因
    2017-03-03
  • C语言中将日期和时间以字符串格式输出的方法

    C语言中将日期和时间以字符串格式输出的方法

    这篇文章主要介绍了C语言中将日期和时间以字符串格式输出的方法,分别是ctime()函数和asctime()函数,注意参数区别,需要的朋友可以参考下
    2015-08-08
  • 详解C++的JSON静态链接库JsonCpp的使用方法

    详解C++的JSON静态链接库JsonCpp的使用方法

    这篇文章主要介绍了C++的JSON静态链接库JsonCpp的使用方法,演示了使用JsonCpp生成和解析JSON的方法,以及C++通过JSON方式的socket通信示例,需要的朋友可以参考下
    2016-03-03

最新评论