C语言进阶知识(五)

指针的进阶

7.回调函数之详细理解

我们今天来详细了解回调函数及其应用,首先我们先来想一想曾经学过的冒泡排序,其基本思想:通过两个元素的比较,将较大值后移,最终实现数组升序的目标。我们用一段代码进行演示。

bubble_sort(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = 0;
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}
int main()
{
	int arr[] = { 3,1,4,5,8,9,2,0,7,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	bubble_sort(arr, sz);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

但是冒泡排序只能实现整型数据,如果是浮点型,结构体怎么进行比较呢?在这里,介绍一个C语言库函数中的函数:qsort;

qsort有四个参数,第一个参数:void* base指向了需要排序的数组的第一个数组,第二个参数:size_t num是排序的元素个数,第三个参数:size_t size指的是排序的一个元素的大小,第四个参数:int (*compar)(const void*,const void*)是函数指针,这个函数指针指向的函数能够比较base指向数组中的两个元素。 当前面元素大于后面元素返回大于0的数,等于返回0;前面元素小于后面元素返回小于0的数。

这里有一个问题void*指针到底代表了什么?其实它指的是无具体类型的指针,它可以指向int,float,等等,因为不知道它具体的类型,所以不能再使用解引用*,和指针加减运算,因为并不能知道它具体类型。

我们再来看一下实际应用时,怎样使用qsort函数进行比较。

(1)使用qsort对数组进行排序

int cmp_int(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
	//既然不知道void*具体代表什么类型,就强制类型转换,然后再解引用得到值
}
//cmp_int函数是对arr数组元素进行比较的函数
void Print(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
test1()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	//arr数组首元素地址,sz数组大小,sizeof求出比较元素字节大小
	Print(arr, sz);
}
int main()
{
	test1();
	return 0;
}

 

(2)使用qsort对结构体进行排序

struct Stu
{
	char name[20];
	int age;
};
int cmp_stu_age(const void* p1, const void* p2)
{
	return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
	//先把p1强制类型转换成结构体指针,再进行解引用找到age
}
void Print(struct Stu arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i].age);
	}
	printf("\n");
}
void test2()
{
	struct Stu arr[] = { {"zhangsan",20},{"lisi",17},{"zhangjing",25} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_age);
	Print(arr, sz);
}
int main()
{
	test2();
	return 0;
}

综上所述,qsort的特点是基于快速排序思想,适用于任何类型的数据进行排序。那么我们今天,基于冒泡排序的思想,模拟实现qsort函数。

那么当模拟实现此函数时就需要进行对照参数进行设计,我们设计的函数名为bubble_sort,那么对照其第一个参数设计为void* base,第二个参数则为int num,第三个参数为int size,第四个参数为int (*compar)(const void*,const void*)。我们将四个参数设计完之后,需要考虑其代码内部实现,qsort函数基于快速排序思想,那我们就基于冒泡排序的思想,处理函数内部。

void Swap(char* buf1, char* buf2, int size)
{
	int i = 0;
	char tmp = 0;
	for (i = 0; i < size; i++)
	{
		tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;//易错
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
	int i = 0;
	//此时num为要排序元素个数,相当于在冒泡排序中sz;这一步相当于跑躺数
	for (i = 0; i < num - 1; i++)
	{
		int j = 0;
		//每一趟内部的比较
		for (j = 0; j < num - i-1; j++)
		{
			//cmp为函数指针,需要传参,原来为arr[j]和arr[j+1]比较,现在则为函数指针
			//相当于需要将两者的地址传给cmp,再让cmp进行比较。
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}
//就比如int arr={1,2,3,4,5,6,7,8,9} base为arr,num为10,size为4
int cmp_int(const void* p1, const void* p2)
{
	return ( * (int*)p1 - *(int*)p2);
}
void Print(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void test()
{
	int arr[10] = { 3,1,5,2,4,7,9,6,8,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	Print(arr, sz);
}
int main()
{
	test();
	return 0;
 }

我们现在逐个分析每个函数的作用,我们用bubble_sort函数代替qsort函数实现排序思想,用冒泡排序代替其快速排序,在bubble_sort中,我们是函数设计者,我们根据冒泡排序核心,实现对四个参数的利用,以及实现排序。Swap函数则是实现交换的思想,剩下的test和cmp_int函数都是我们为了验证bubble_sort函数写出的函数,是从应用角度出发,理解这个概念后,我们来看一些里面比较复杂的代码。

if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
{
    Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
}

这一段函数是核心,其核心思想利用bubble_sort函数传进来的函数指针,进行对元素的比较,由于bubble_sort可实现对所有数据的排序,所有我们要使用指针,而不是像冒泡排序那样直接进行比较,if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)这一段函数就实现了判断,之所以这里使用char*是为了解引用时候只读一个字节,这样可以和后面的相乘联系起来。大家还有什么问题?欢迎在评论区讨论。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值