【C语言进阶】指针的进阶(下)

衔接上文:

        1.函数指针数组

        2.指向函数指针数组的指针

        3.回调函数 (qsort函数)

目录

前言

一、函数指针数组

1.1认识函数指针数组

1.2函数指针数组的应用

二、指向函数指针数组的指针

三、回调函数

3.1qsort函数

3.1.1qsort函数的定义

3.1.2使用回调函数,模拟实现qsort(冒泡的方式)

3.1.3qsort算法的实现

总结


前言

上篇文章,我们学习了字符指针、指针数组、数组指针、函数指针,接下来我们来一起探讨更深层次的指针进阶的内容!!!

一、函数指针数组

1.1认识函数指针数组

前面我们学到了函数指针,函数指针是存放函数地址的指针,其表现形式为:

int*(*pa)(int,int)= &add        表示将add函数的地址传递给pa这个函数指针,这个函数的形参为两个int类型,返回参数为int类型的数据,这就是函数指针。

应用:

1.int len =(*pa)(2,1)         表示的是使用add函数传参为2,1 然后返回的数值给 len

2.int len = pa(2,1)             因为pa其实就是得到函数的地址,正常函数的使用的时候,也是函数名加传参,函数名的地址和取地址函数名的地址是一样的,所以是可以不需要解引用(*pa),这个星号是无所谓的,即使是******pa也不会影响结果

接下来,是对于函数指针数组的应用

数组是存放相同类型数据的存储空间,我们已经学习了指针数组

如:

     int*arr[10]       表示存放十个整型指针的数组arr

所以我们可以打开猜想一下,函数指针数组应该是什么样的呢? 

1.2函数指针数组的应用

//函数指针数组的用途: 转移表(相当于一个中介,连接一个函数到另一个函数)

如何转换,转换的是什么?步骤是什么?

演示:

二、指向函数指针数组的指针

指向函数指针数组的指针是一个指针

指针指向一个数组,数组的元素都是函数指针

void test(const char* str)
{
	printf("%s\n", str);
}
int main()
{
	//函数指针pfun
	void (*pfun)(const char*) = test;
	//函数指针的数组pfunArr
	void (*pfunArr[5])(const char* str);
	pfunArr[0] = test;
	//指向函数指针数组pfunArr的指针ppfunArr
	void (*(*ppfunArr)[5])(const char*) = &pfunArr;
	//(*ppfunArr)  ppfunArr与*先结合,说明这是一个指针,剩下void (*[5])(const char*)  实际上是 void (*)(const char*) [5]函数指针数组
	//所以 void(*(ppfunArr)[5])(const char*) 这是个指向函数指针数组的指针

	return 0;

我们可以发现,(ppfunArr)与【】先结合的时候这是函数指针数组,当与*先结合的时候,这就是指向函数指针数组的指针,确定或者使用这一类的指针数组、数组指针,第一步是确认ppfunArr先和谁结合,确定到底是数组还是指针,然后向外刨析

我们可以发现,(ppfunArr)与【】先结合的时候这是函数指针数组,当与*先结合的时候,这就是指向函数指针数组的指针

判别这一类类型的方法:

        1.确认ppfunArr先和谁结合,确定到底是数组还是指针

        2.如果是【】说明这个整体是数组,如果是 * 说明整体是指针

        3.然后向外刨析,一个括号一个括号的破开

        4.void(*pa)()实际上是  void(*)()   pa  前者为函数类型,后者pa是指针名 

三、回调函数

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

#include<stdio.h>
int sub(int x, int y) {
	return  x - y;
}
int add(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;
}
void coul(int(*pa)(int, int)) {
	int x, y;
	scanf("%d %d", &x, &y);
	int len=pa(x, y);
	printf("%d\n", len);
}
int main()
{	//函数指针 pa
	int(*pa)(int, int) = add;
	//函数指针数组
	int(*ppa[10])(int, int) = { add,sub,mul,div };
	//ppa[10]  ppa 先于[]相结合 说明这个是个数组 剩下元素为 int(*)(int,int)说明存放的是函数指针类型
	//所以这就是可以存放十个以两个形参为int类型,返回类型为int的,函数指针数组,ppa(数组名)
	
	//对于函数指针数组的应用
	int x, y;
	int input = 1;
	int ret = 0;
	do {
		printf("*************************\n");
		printf(" 1:add       2:sub \n");
		printf(" 3:mul       4:div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		switch (input) {
		case 1:
			coul(add);  //使用回调函数,使得main函数与要实现的add或者sub等函数建立起来桥梁
			break;
		case 2:
			coul(sub);
			break;
		case 3:
			coul(mul);
			break;
		case 4:
			coul(div);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);

	return 0;
}

3.1qsort函数

3.1.1qsort函数的定义

 qsort函数的使用,需要 <stdio.h><search.h>两个头文件

需要传递的数据为:

1.void* base          目标数据(需要排序的)

2.size_t num         排序的数据的元素个数,如arr[10],就传参为10

3.size_t width        排序数据的元素所占字节的大小,如 int arr[10],传参为4

4.int (__cdecl *compare )(const void *elem1, const void *elem2 )

   传递的是函数指针,实参为两个被const修饰的无符号指针类型,void*是为了普适性,可     以接收所有类型的数据

接下来是代码实现:

#include<stdio.h>
#include<search.h>
int compare(const void* e1, const void* e2) {
	//使用 const是防止修改e1,e2的指针指向的地址,void*(无符号指针)表示函数的普适性
	//		可以接收所有类型的指针。
	return *(int*)e2 - *(int*)e1;
	//(int*)是因为是对void*类型进行强制转换,然后*解引用,比较e1 和 e2 的大小
}
int main()
{
	int arr[] = { 1,2,31,45,55,6,7,8,9,10,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), compare);
	for (int i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	return 0;
}

3.1.2使用回调函数,模拟实现qsort(冒泡的方式)


//使用回调函数,模拟实现qsort(采用冒泡的方式)

//比较函数
int compare(const void* e1, const void* e2) {
	return *(int*)e1 - *(int*)e2;
}
//交换函数
void swap(void* e1, void* e2, int width) {
	for (int i = 0; i < width; i++) {
		//一个元素 width个字节,自然是width次转换
		char tmp = (*((char*)e1 + i));
		(*((char*)e1 + i)) = (*((char*)e2 + i));
		(*((char*)e2 + i)) = tmp;
	}
}
//使用冒泡排序的方式模拟实现qsort函数
void buffer_qsort(void* base, size_t num, size_t width, int (*compare)(const void* e1, const void* e2)) {
	for (int i = 0; i < num-1; i++) {
		for (int j = 0; j < num - 1 - i; j++) {
			//冒泡排序基本的循环是不用变的,只需要改变交换的方式
			if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0) {
				//传递数据的适合因为不知道传参的是什么类型,所以就是用表示一个字节的char*进行强制类型转换
				//(char*)base  取的是数组的首元素的第一个字节,然后加上j*width,相当于跳过该类型的元素,找到下一个元素
				//进行compare函数比较
				//swap函数进行交换
				
				swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
			//除了传递每一个元素(char*)base + j * width 也要传递一个数组元素字节为多少 width
				//我们的交换原理就是创建一个临时变量 width次循环交换 

			}
		}
	}
}
int main()
{
	int arr[] = { 11,2,3,4,45,62,7,8,8,9 };
	int sz = sizeof(arr) / sizeof(arr[1]);
	buffer_qsort(arr, sz, sizeof(arr[1]), compare);
	for (int i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	return 0;
}



3.1.3qsort算法的实现

具体介绍,请看博主主页快排那一章的内容,下面仅仅是演示快排算法

void quick_sort(int* arr, int l, int r) {
	if (l >= r) {
		return;
	}
	int k = arr[l];
	int i = l - 1;
	int j = r + 1;
	
	while (i < j) {
		do {
			i++;
		} while (arr[i] < k);
		do{
			j--;
		} while (arr[j] > k);
		if (i < j) {
			int tmp = arr[i];
			arr[i] = arr[j];
			arr[j] = tmp;
		}
	}
	quick_sort(arr, l, j);
	quick_sort(arr, j + 1, r);
}
int main()
{
	int arr[] = { 1,25,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	quick_sort(arr, 0, sz-1);
	for (int i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	return 0;
}

总结

那么到此为止,指针进阶的内容就结束啦,完结撒花!!!

我们在这边学习了,字符数组指针数组数组指针函数指针函数指针数组指向函数指针数组的指针,以及回调函数的定义,和通过冒泡排序模拟实现qsort函数讲解qsort函数的原理,以及对于quick_sort快排算法的补充

博主提前预告哦,接下来是对于字符串的一系列函数的学习,让我们拭目以待!!!

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小王学代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值