C语言:指针详解【进阶】后篇

这篇具有很好参考价值的文章主要介绍了C语言:指针详解【进阶】后篇。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言:

在C语言:指针详解【进阶】前篇中我们深入学习了字符指针数组指针指针数组以及数组传参和指针传参。我们对指针的应用有了较为深刻的认识,今天这里我们将更加深入的进行对更复杂的指针的探究。

函数指针

在前面我们知道一个指针变量可以指向一块内存的地址,我们也知道函数在使用时也要向内存申请一块内存空间,那我们不妨想一想我们能不能创建一个指针变量来指向一个函数呢?
我们先来看看函数的地址到底是什么?

#include <stdio.h>
void test()
{
	printf("hehe\n");
}
int main()
{
	test();
	printf("%p\n", &test);
	printf("%p\n", test);
	return 0;
}

C语言:指针详解【进阶】后篇
这里我们分别打印&testtest的值,结果两者相同,那我们可以认为&testtest都代表的是函数test的地址,这里是两者没有区别的。

当我们知道函数的地址后,我们就可以创建一个指针来指向这个函数了,但问题是函数的形式有很多种,我们的函数指针的数据类型该如何定义呢?
这里的定义其实与数组指针的定义是类似的,在数组指针的定义时,我们把数组指针的数组名取出来后,剩下的就是数组的数据类型了
那么这里我们把函数的函数名取出来,那剩下的就是函数的数据类型,这样就可以来定义函数指针了。
C语言:指针详解【进阶】后篇

函数指针在使用时可以先解引用指针pf,再在后边带上参数就行:

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf)(int , int) = &Add;
	//int (*pf)(int x, int y) = &Add;
	//int (*pf)() = &Add;
	//这三种定义方法都可行
	
	int ret1 = (*pf)(3, 5);
	printf("%d\n", ret1);
	return 0;
}

C语言:指针详解【进阶】后篇

我们前面得知&testtest都代表的是函数test的地址,这里是两者没有区别的。那么这里我们的函数指针的使用就还有另一种方式,不用解引用pf,直接使用:

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf)(int , int) = &Add;
	int ret1 = (*pf)(3, 5);
	printf("%d\n", ret1);
	int ret2 = pf(3, 5);
	printf("%d\n", ret2);
	return 0;
}

C语言:指针详解【进阶】后篇
其实这里的语句 int ret1 = (*pf)(3, 5);中的*就是一个摆设,没有实质性的用途,这里的*只是为了帮助我们来理解这个语句的。

所以这里我们可以任意的增加和删减*, 都是不影响结果的。
但是注意:如果你要使用*,那就必须把 *pf()括起来。

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf)(int , int) = &Add;
	int ret1 = (*****pf)(3, 5);
	printf("%d\n", ret1);
	return 0;
}

C语言:指针详解【进阶】后篇
当我们学会函数指针后,你可能会疑惑,函数指针的使用场景是什么?我们为何不直接调用函数来使用呢?
其实函数指针本质是一个指针,我们指针的使用场景就是将数据传递到另一个函数中去使用,那当我们需要传递一个函数作为参数到另一个函数中时,就需要用到函数指针了
这里关于函数指针的使用就放在回调函数的板块讲解了。
这里我们分析两个有趣的的函数指针:

int main()
{
	//代码1
	(*(void(*)())0)();
	//代码2
	void (*signal(int, void(*)(int)))(int);
	return 0;
}

我们将代码1进行分解一下就好理解了:
C语言:指针详解【进阶】后篇
代码1其实就是一次函数调用。


我们再对代码2进行分解:
C语言:指针详解【进阶】后篇
代码2其实就是一次函数声明。
其实对于代码2,可以进行优化一下,使我们能更好的读懂代码;
这里我们要使用一个自定义关键词typedef(重命名)来操作。
我们将这个函数的返回值void(*)(int)进行重命名为ptr_t来进行简化代码。

//typedef void (*)(int) ptr_t; //错误写法
typedef void (*ptr_t)(int);
int main()
{
	ptr_t signal(int, void(*)(int));
	return 0;
}

注意:

在对返回值是函数指针的类型重命名时,新名字要放在函数指针的内部,不能放在后边,这样才符合语法。


函数指针数组

前面我们学习了指针数组,该数组内部可以放置相同类型的指针,我们今天又学习了函数指针,那我们是否可以创建一个函数指针数组来存放函数指针呢?答案是可以的。
函数指针数组的定义与指针数组的定义是一样的,都要有:数组元素类型,数组名,数组元素个数

void test1()
{}
void test2()
{}
void test3()
{}
void test4()
{}
int main()
{
	void (*pf1)() = &test1;
	void (*pf2)() = &test2;
	void (*pf3)() = &test3;
	void (*pf4)() = &test4;
	void *parr[4]() = { pf1,pf2,pf3,pf4 };
	void (*)() parr[4] = { pf1,pf2,pf3,pf4 };
	void (*parr[4])() = { pf1,pf2,pf3,pf4 };
	//这三种函数指针数组的定义写法,哪种是正确的?
	return 0;
}

这里和函数指针的声明类似,变量名要紧挨着*,只是这里变量名先和[]结合,作为一个数组。
所以这里的第三种定义是正确的。

void (*parr[4])() = { pf1,pf2,pf3,pf4 };

关于函数指针数组的用途:
我们知道函数指针数组中存放的是统一类型的函数指针,那么我们对于某一类数据进行相似操作时,就可以使用到函数指针数组来方便的调用一些函数了,同时有助于减少相似代码的书写,简化程序。
这里就举例实现一个简易计算器来说明函数指针数组的用途:

这里是函数的编写及头文件的声明:

#include <stdio.h>
void menu()
{
	printf("*********************************\n");
	printf("*******   1.Add   2.Sub   *******\n");
	printf("*******   3.Mul   4.Div   *******\n");
	printf("*******   0.Exit          *******\n");
	printf("*********************************\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)
{
	return x / y;
}

版本一(不使用函数指针数组):

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	do
	{
		menu();
		printf("请选择:> ");
		scanf("%d", &input);
		switch (input)
		{
			case 0:
			{
				printf("退出计算器\n");
				break;
			}
			case 1:
			{
				printf("请输入操作数:> ");
				scanf("%d %d", &x, &y);
				printf("%d\n", Add(x, y));
				break;
			}
			case 2:
			{
				printf("请输入操作数:> ");
				scanf("%d %d", &x, &y);
				printf("%d\n", Sub(x, y));
				break;
			}
			case 3:
			{
				printf("请输入操作数:> ");
				scanf("%d %d", &x, &y);
				printf("%d\n", Mul(x, y));
				break;
			}
			case 4:
			{
				printf("请输入操作数:> ");
				scanf("%d %d", &x, &y);
				printf("%d\n", Div(x, y));
				break;
			}
			default:
			{
				printf("输入错误,请重新输入!\n");
			}
		}
	} while (input);
	
	return 0;
}

版本二(使用函数指针数组):

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int (*parr[5])(int, int) = { NULL, Add, Sub, Mul, Div };
	do
	{
		menu();
		printf("请选择:> ");
		scanf("%d", &input);
		if(0 < input && input < 5)
		{
			printf("请输入操作数:> ");
			scanf("%d %d", &x, &y);
			printf("%d\n", parr[input](x, y));
		}
		else if (input == 0)
		{
			printf("退出计算器\n");
			break;
		}
		else
		{
			printf("输入错误,请重新输入!\n");
		}
	} while (input);
	return 0;
}

两者的代码一比较就可以发现,使用函数指针数组后,代码中的重复代码大大下降,代码整体简洁清晰了不少。

这里就体现了函数指针数组的用途:转移表


指向函数指针数组的指针

我们刚刚学习了函数指针数组,我们是否可以用一个指针来指向这个数组?那这个指向函数指针数组的指针又该如何定义呢?

#include <stdio.h>
void test(const char* str)
{
	printf("%s\n", str);
}
int main()
{
	 //函数指针pfun
	 void (*pfun)(const char*) = test;//&test 和 test 是一样的
	 //函数指针的数组pfunArr
	 void (*pfunArr[5])(const char* str);
	 pfunArr[0] = test;
	 return 0;
}

这里的定义其实与指针的定义是类似的,在数组指针的定义时,我们把数组指针的数组名取出来后,剩下的就是数组的数据类型了
那么这里我们把函数指针数组的函数名取出来,那剩下的就是函数指针数组的数据类型,这样就可以来定义函数指针数组的指针了。
C语言:指针详解【进阶】后篇

#include <stdio.h>
void test(const char* str)
{
	printf("%s\n", str);
}
int main()
{
	 //函数指针pfun
	 void (*pfun)(const char*) = test;//&test 和 test 是一样的
	 //函数指针的数组pfunArr
	 void (*pfunArr[5])(const char* str);
	 pfunArr[0] = test;
	 //指向函数指针数组pfunArr的指针ppfunArr
	 void (*(*ppfunArr)[5])(const char*) = &pfunArr;
	 return 0;
}

回调函数

回调函数就是一个通过函数指针调用的函数。

就是上面所说的使用函数指针进行传参的应用。
解释:

如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

这里我们来进行分析一个qsort函数(库函数),进行对回调函数的了解。
这里我们需要先去了解一下qsort函数, 明白这个函数是什么用处,又该如何使用?
我们打开cplusplus网站,进行搜索。
C语言:指针详解【进阶】后篇
得知qsort函数是一个排序的函数。
注意:它可以排序任意类型的数据。
函数使用:
在使用qsort函数时,我们需要只知道要排序的第一个元素的地址,要排序元素的个数,每个元素的大小,以及一个能比较两个元素的大小的函数。
这里我们唯一要设计的就是编写一个能比较两个元素的大小的函数。
注意:设计的函数要和库里给定的该比较函数模板格式要一致。

#include <stdio.h>
#include <stdlib.h>
int cmp_int(const int* p1, const int* p2)
{
	return *p1 - *p2;
}
int main()
{
	int arr[10] = { 7,6,1,2,8,9,3,5,0,4 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(int), cmp_int);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

C语言:指针详解【进阶】后篇
这里我们设计的cmp_int函数就是回调函数。


讲到这里指针详解【进阶】后篇的知识讲解就结束了。
关于指针的知识是重点,也是难点,不仅仅是知识的了解,更要进行大量的练习才能巩固知识。
这几天我会出一期关于指针进阶习题的练习和讲解,来进行加深对指针的更进一步的记忆。
同时对于qsort函数,我们不仅仅只是会使用它,还要学会去自己实现一个qsort函数。同样会放在指针进阶习题的练习和讲解文章之后马上更新。


感兴趣的的小伙伴点点赞,点点关注,谢谢大家的阅读哦!!!
点点关注,后期不错过哦。😘
你们的鼓励就是我的动力,欢迎下次继续阅读!!!😘😘😘文章来源地址https://www.toymoban.com/news/detail-445578.html

到了这里,关于C语言:指针详解【进阶】后篇的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C语言】进阶指针,超详解,含丰富代码示例

    这里是初阶的链接,方便大家对照查看!!!添加链接描述 大家好呀,今天和大家将指针进阶的知识进行分享,这块是指针的难点部分,希望博主对其的理解可以帮助到大家!!! 字符指针 数组指针 指针数组 函数指针 函数指针数组 指向函数指针数组的指针 在指针的类型

    2024年02月13日
    浏览(42)
  • 【进阶C语言】进阶指针--学会所有指针类型

     本节内容大致目录: 1.字符指针 2.指针数组(数组) 3.数组指针 (指针)--比较重要 4.函数指针--比较重要 5.函数指针数组--用的较少 6.指向函数指针数组的指针--只需要了解就可以 需要掌握每一种类型的符号和用处。 前言:字符指针是一种指针,是众多指针类型中的一种。

    2024年02月07日
    浏览(42)
  • 【C语言进阶】 指针进阶(二)

    🔥 博客主页: 小王又困了 📚 系列专栏: C语言 🌟 人之为学,不日近则日退  ❤️ 感谢大家点赞👍收藏⭐评论 ✍️ 目录 一.函数指针  1.1函数指针的认识 1.2函数指针的使用 二、函数指针数组 1.1函数指针的认识 1.2 函数指针数组实现计算器  三、指向函数指针数组的指针

    2024年02月15日
    浏览(42)
  • 『C语言进阶』指针进阶(二)

    🔥 博客主页 : 小羊失眠啦 🔖 系列专栏 : C语言 🌥️ 每日语录 : 无论你怎么选,都难免会有遗憾。 ❤️ 感谢大家点赞👍收藏⭐评论✍️ 在上篇指针进阶中,我们对字符指针、指针数组、数组指针以及数组传参和指针传参有了一定的了解,你以为指针进阶就只有这些内

    2024年02月07日
    浏览(39)
  • 『C语言进阶』指针进阶(一)

    🔥 博客主页 : 小羊失眠啦 🔖 系列专栏 : C语言 🌥️ 每日语录 : 无论你怎么选,都难免会有遗憾。 ❤️ 感谢大家点赞👍收藏⭐评论✍️ 在C语言初阶中,我们对指针有了一定的了解,指针是个变量,是用来存放地址的,指针的大小是固定的4/8个字节,指针是有类型的

    2024年02月09日
    浏览(36)
  • 【C语言】指针进阶:字符指针&&数组指针&&函数指针

    ✨作者:@平凡的人1 ✨专栏:《C语言从0到1》 ✨一句话:凡是过往,皆为序章 ✨说明: 过去无可挽回, 未来可以改变 🌹 感谢您的点赞与关注,同时欢迎各位有空来访我的 🍁平凡舍 回想之前,我们学了 指针 的一些基础👉 指针与结构体 我们知道了指针的概念: 指针就是

    2023年04月08日
    浏览(43)
  • 【C语言进阶】指针数组 —— 数组指针

    🎬 鸽芷咕 : 个人主页  🔥 个人专栏 : 《C语言进阶篇》 《C语言初阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,前面我们已经把指针大部分内容给学完了,今天就给大家带来数组指针or指针数组!    ⛳️ 很多说这俩名字不是差不

    2024年02月14日
    浏览(38)
  • 进阶C语言-指针的进阶(一)

    通过前面的学习,我们了解了指针的概念: 指针变量就是一个变量,用来存放地址,地址唯一标识一块内存空间。 指针的大小是固定为 4/8 个字节( 32位平台/64位平台 )。 指针是有类型的,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。 指针的运算

    2024年02月07日
    浏览(40)
  • C语言进阶之指针的进阶

    指针的主题,我们在C语言初阶博客已经接触过了,我们知道了指针的概念: 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。 指针的大小是固定的4/8个字节(32位平台/64位平台)。 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候

    2024年02月16日
    浏览(39)
  • 进阶C语言-指针的进阶(二)

    数组指针 - 指向数组的指针 - 存放的是数组的地址 - 数组名就是数组的地址。 函数指针 - 指向函数的指针 - 存放的是函数的地址 - 函数名就是函数的地址。 🔭阅读下面这两段代码: 🔭 指针数组: 字符指针数组 - 数组 - 存放的是字符指针 整型指针数组 - 数组 - 存放的是整

    2024年02月07日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包