【C语言】14. 指针深入(上)



一、回调函数是什么?

如果将函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,被调⽤的函数就是回调函数。在上一篇文章中,转移表实现计算器就是使用了回调函数。

相似的代码可以抽象成函数:有了函数指针后,函数的调用,可以使用函数名来调用,也可以使用函数指针来调用。

二、qsort函数

qsort是专门用来排序的库函数,可以直接排序任何类型的数据。它的底层逻辑是快速排序。
qsort函数的使用需要包含头文件stdlib.h

1、排序整型数据

接下来我们看一下qsort函数的原型:

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

总共有四个参数:
void* base:数组首元素地址。
size_t num:数组的元素个数。
size_t size:单个元素的大小。
int (*compar)(const void*,const void*));:比较函数compar,前-后为升序,后-前为降序。
注:比较函数的参数类型是void*,因此传入参数时要根据参数的类型从而对参数进行强制类型转换。

接下来就使用qsort函数排序整型数据:

#include<stdio.h>
#include<stdlib.h>

//比较函数
int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;//升序
}

int main()
{
	int arr[10] = { 2,6,7,1,8,9,10,5,4,3 };
	int len = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, len, sizeof(arr[0]), cmp_int);

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

运行结果:
在这里插入图片描述

2、排序结构体数据

定义一个学生的结构体:

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

这里结构体有两个元素,那么该按谁来进行排序呢?
①按照名字排序:比较函数使用strcmp(字符串比较)。
②按照年龄排序:就是上面的整型数据排序。

如下:

#include<stdio.h>
#include<stdlib.h>//qsort函数
#include<string.h>//strcmp函数

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

int cmp_by_age(const void* p1, const void* p2) 
{
    return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;//升序
}

int cmp_by_name(const void* p1, const void* p2)
{
    return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);//升序
}

int main()
{
    struct Stu arr[3] = { {"zhangsan", 20}, {"lisi", 35}, {"wangwu", 18} };
    int len = sizeof(arr) / sizeof(arr[0]);

    //按年龄排序
   /* qsort(arr, len, sizeof(arr[0]), cmp_by_age);*/
    //按名字排序
    qsort(arr, len, sizeof(arr[0]), cmp_by_name);

    for (int i = 0; i < len; i++)
    {
        printf("Name: %s, Age: %d\n", arr[i].name, arr[i].age);
    }
    return 0;
}

运行结果:
在这里插入图片描述

三、模拟实现qsort函数

我们先来观察冒泡排序的代码:

void bubble_sort(int arr[], int len)
{
	for (int i = 0; i <len - 1; i++)
	{
		for (int j = 0; j < len - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

上述冒泡排序可以用来排序整数数据。
那么如果我要改造这个冒泡函数,让他能够排序任意类型的数据,该怎样改造呢?
我们知道两个整型元素可以直接比较大小,但是两个字符串、两个结构体元素是不能直接比较大小的。

那么我们可以把两个元素比较的方法,先封装成函数,然后再把函数的地址传给排序函数,就可以实现排序了。

如下:

#include<stdio.h>
#include<stdlib.h>

//比较函数
int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}

//交换函数
void Swap(char* buf1, char* buf2, size_t width)
{
	//一次交换一个字节(可能是字符)
	for (int i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

//模拟qsort函数
void my_qsort(void* base, size_t len, size_t width, int(*cmp)(const void* p1, const void* p2))//cmp是要传的比较函数的参数
{
	//冒泡排序
	//躺数
	for (int i = 0; i < len; i++)
	{
		//排一个最大或最小数
		for (int j = 0; j < len - i - 1; j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)//调用比较函数比较前后两个字节(可能是字符),选择升降序
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

int main()
{
	int arr[] = { 2,6,7,1,8,9,10,5,4,3 };
	int len = sizeof(arr) / sizeof(arr[0]);
	my_qsort(arr, len, sizeof(arr[0]), cmp_int);

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

运行结果:
在这里插入图片描述

这样便实现了对整型数据进行升序的效果。

同样这个模拟qosrt实现的冒泡排序算法也能对结构体元素进行比较,只需要将修改比较函数即可。

如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

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

//比较函数-年龄
int cmp_by_age(const void* p1, const void* p2) 
{
    return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age; 
}

//比较函数-姓名
int cmp_by_name(const void* p1, const void* p2) 
{
    return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}


//交换函数
void Swap(char* buf1, char* buf2, size_t width)
{
    //一次交换一个字节(可能是字符)
    for (int i = 0; i < width; i++)
    {
        char tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}

//模拟qsort函数
void my_qsort(void* base, size_t len, size_t width, int(*cmp)(const void* p1, const void* p2))//cmp是要传的比较函数的参数
{
	//冒泡排序
	//躺数
	for (int i = 0; i < len; i++)
	{
		//排一个最大或最小数
		for (int j = 0; j < len - i - 1; j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)//调用比较函数比较前后两个字节(可能是字符),选择升降序
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

int main()
{
    struct Stu arr[3] = { {"zhangsan",20},{"lisi",35},{"wangwu",18} };
    int len = sizeof(arr) / sizeof(arr[0]);
     my_qsort(arr, len, sizeof(arr[0]), cmp_by_name);

    for (int i = 0; i < len; i++)
    {
        printf("Name: %s, Age: %d\n", arr[i].name, arr[i].age);
    }
    return 0;
}

按名字排序:
在这里插入图片描述

传参时传根据年龄的比较函数,就可以按年龄排序:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值