深入理解指针(四)

目录

1.回调函数的实现

2.qsort的使用

2.1简单了解qsort

2.2使用qsort函数对结构体类型数据进行排序

利用age来进行排序

用姓名来进行排序

3.模拟实现qsrot函数


1.回调函数的实现

什么是回调函数,回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数 时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条 件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。

例如,分析下面代码,case1、case2等,他们都用很多相同的语句,这样过于冗余,那么回调函数可以解决这种冗余。

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(" 0:exit \n");
	printf("*************************\n");
	printf("请选择:");
	scanf("%d", &input);
	switch (input)
	{
	case 1:
		printf("输⼊操作数:");
		scanf("%d %d", &x, &y);
		ret = arr(x, y);
		printf("ret = %d\n", ret);
		break;
	case 2:
		printf("输⼊操作数:");
		scanf("%d %d", &x, &y);
		ret = arr2(x, y);
		printf("ret = %d\n", ret);
		break;
	case 3:
		printf("输⼊操作数:");
		scanf("%d %d", &x, &y);
		ret = arr3(x, y);
		printf("ret = %d\n", ret);
		break;
	case 4:
		printf("输⼊操作数:");
		scanf("%d %d", &x, &y);
		ret = arr4(x, y);
		printf("ret = %d\n", ret);
		break;
	case 0:
		printf("退出程序\n");
		break;
	default:
		printf("选择错误\n");
		break;
	}
} while (input);

使用回调函数:

void calc(int (* pf)(int , int)) //传过来的是函数的地址,用函数指针来接收
{

	int x, y;
	int ret = 0;
	printf("输入操作数:");
	scanf("%d %d", &x, &y);
	ret = (*pf)(x, y);//ret = pf(x, y);
	printf("ret = %d\n", ret);
	
}

int main()
{
	//指针的运用,回调函数实现
	int input = 1;
		do
		{
			printf("*************************\n");
			printf(" 1:arr 2:arr2 \n");
			printf(" 3:arr3 4:arr4 \n");
			printf(" 0:exit \n");
			printf("*************************\n");
			printf("请选择:");
			scanf("%d", &input);
			switch (input)
			{
			case 1:
				calc(arr);//传过去的是函数的地址。
				break;
			case 2:
				calc(arr2);
				break;
			case 3:
				calc(arr3);
				break;
			case 4:
				calc(arr4);
				break;
			case 0:
				printf("退出程序\n");
				break;
			default:
				printf("选择错误\n");
				break;
			}
		} while (input);
	
	
	return 0;
}

2.qsort的使用

2.1简单了解qsort

qsort函数可以实现任意类型数据的排序
qsort( void *p ,num,sz, const void* a, const void* b),qsort含有四个参数
四个参数分别是
第一个是void 类型的指针,用于指向排序的数组首元素的地址,为啥是void呢,那就是要接收任意类型的数据。
第二个是 指针指向的数组的个数,用sizeof来求
第三个是指针指向的数组的类型的字节数。用sizeof,例如sizeof(a[0]).
第四个是函数指针,要提供一个函数可以对数据进行对比的函数。(> 0,= 0,0<   )

深入理解指针(四)_第1张图片

qsort写法

void qsort (void *base, size_t nitems, size_t size, int (*compar) (const void *, const void*)) 

int paixu(const void* p1,const void* p2)
{
	//在这里最重要的是把数据进行强制转换类型,转换成int类型再进行比较
	return *(int*)p1 - *(int*)p2; //这里进行转换并且解引用进行比较
}


int main()
{
int arr[10] = { 5,4,8,1,3,9,0,2,6,7 };
int sz = sizeof(arr) / sizeof(arr[0]);//元素个数
int SZ = sizeof(int);//字节大小
qsort(arr, sz, SZ,paixu );
for (int i = 0; i < sz; i++)
{
	printf("%d  ", arr[i]);
}
return 0;
}

深入理解指针(四)_第2张图片总结一下,qsort本质其实就是能接收任意类型的数据并将其排序,内部其实就是将接收到的数据进行强制类型的转换成int,然后对指针解引用进行比较。

2.2使用qsort函数对结构体类型数据进行排序

使用qsort函数实现结构体数据的排序
用结构体赖比较的话,要搞明白用什么来比较
第一个如果是int类型那么可以直接比较大小 ,例如年龄来比较 可以用 < = >

第二个如果是字符类型那么就要用到库函数strcmp来比较字符串的大小,例如,姓名

利用age来进行排序

#include
#include//qsort头文件

struct stu
{
	char name[20];
	int age;
};

//用年龄的方法来比较
int cmp_age(const void* p1, const void* p2) //大于则返回>0,等于返回0,小于返回0 <
{
	//强制转换类型为结构体指针,这样才可以找到年龄来比较
	return ((struct stu*)p1)->age - ((struct stu*)p2)->age; //这里记得要括号起来才可以
    //用箭头找到成员age, 结构体指针 -> 结构体成员
	//默认升序,如果降序可以反过来
	//return ((struct stu*)p2)->age - ((struct stu*)p1)->age;
}
void ax()
{
	struct stu arr[] = { {"zhangsan",19},{"lisi",28},{"laowu" ,21} };//初始化
	int sz = sizeof(arr) / sizeof(arr[0]);//求元素个数
	int SZ = sizeof(arr[0]);//求字节大小
	struct stu* p1 = arr; //结构体类型的指针指向结构体类型的arr地址。

	qsort(arr, sz, SZ,cmp_age ); //第四个参数是放一个比较的方法函数,年龄来比较
    //打印
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d \n", p1[i].name,p1[i].age);
	}


}

int main()
{
	ax();
	
	return 0;
}

用姓名来进行排序

#include
#include//strcmp头文件
#include//qsort头文件
struct stu
{
	char name[20];
	int age;
};
//用姓名字符串来比较
int cmp_name(const void* p1, const void* p2)
{
	//把两个数据放入strcmp函数即可。
	//strem  比的是对应位置的ascll码大小,而不是长度
	return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}

void ax()
{
	struct stu arr[] = { {"zhangsan",19},{"lisi",28},{"laowu" ,21} };//初始化
	int sz = sizeof(arr) / sizeof(arr[0]);//求数组元素个数
	int SZ = sizeof(arr[0]); //求字节大小
	struct stu* p1 = arr; //结构体类型的指针指向结构体类型的arr地址。

	qsort(arr, sz, SZ, cmp_name); //姓名比较
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d \n", p1[i].name,p1[i].age);
	}


}
int main()
{
	ax();
	
	return 0;
}

3.模拟实现qsrot函数

        冒泡排序只能对整数的数据进行排序,而不能做到对任意数据类型进行排序,所以要改造成可以排任意数据类型

使⽤回调函数,模拟实现qsort(采⽤冒泡的⽅式)。

注意:这⾥第⼀次使⽤ void* 的指针,讲解 void* 的作⽤。

void print_arr(int* p, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
}

//比较相邻数的大小
int cmp1(const void* p1, const void* p2)//这里类型要为int,还要进行判断然后返回去,类似于qsort函数
{
	//在这里统一要转换成int类型的指针然后解引用
	return *(int *)p1  - *(int *)p2 ;
	//如果p1-p2 大于0那么返回 >0
	//如果p1-p2 小于于0那么返回 0<
	//如果p1-p2 等于0那么返回 =0
}


//进行两个数的交换
void Swap(char *buf1 , char *buf2, size_t zj)
{
	//有几个字节那就换几个,因为都统一改成char类型
	//所以通过一个一个字节的交换就可以了
	//例如int类型,四个字节,需要切成四个字节,然后一个一个字节的交换
	int i = 0;
	for (i = 0; i < zj; i++)
	{
		char  tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;//地址要下一个
		buf2++;//这里也是下一个


	}

}

void ax(void* base, size_t sz, size_t zj ,int (*cmp) (const void *p1 ,const void *p2) ) //base void类型为了接收任意的数据类型
{
	int i = 0;
	//基于冒泡排序框架,两个循环不会改变,改变的是条件而已
	for (i = 0; i < sz-1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//判断的话用到类似于qsort的玩法
			//这里要实现arr[j] 与arr[j+1]的比较,就是相邻的两个数比较
			//(char *)base这个为首地址 ,(char *)base + j*zj 加上后面的j+zj就可以找到第一个了,第二个同理可以找到
			if (cmp((char*)base + j * zj, (char*)base + (j + 1) * zj) > 0)  //找地址,全部统一强制类型转换成char类型,因为char只有一个字节好操作。
			{//如果大于0说明要换位置,下面进行实现交换
				Swap((char*)base + j * zj, (char*)base + (j + 1) * zj, zj);//地址也是放这个,因为要想交换数据,必须通过地址来改。

			}

		}
	}


}


int main()
{
	//char arr[] = { 'I',' ','a','m',' ','a','p','p','y' };
	//char arr[] = ;
	int arr[] = { 5,1,4,8,3,7,6,0,9,2 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int zj = sizeof(arr[0]);
	//size_t为无符号整数,typedef unsigned int size_t;
	ax(arr, sz , zj,cmp1);//这里要用到回调函数,第四个参数要放入一个函数指针用于回调使用。
	print_arr(arr, sz);


	return 0;
}

你可能感兴趣的:(c语言,算法,visual,studio)