
🌟菜鸟主页:@晨非辰的主页
👀学习专栏: 《C语言学习》
💪学习阶段:C语言方向初学者
⏳名言欣赏:"暴力解法是上帝给的,优化解法是魔鬼教的。"
目录
[1. 回调函数](#1. 回调函数)
[1.1 什么是回调函数](#1.1 什么是回调函数)
[1.2 回调函数试验:改造一般方式实现计算器功能](#1.2 回调函数试验:改造一般方式实现计算器功能)
[2. qsort函数](#2. qsort函数)
[2.1 介绍qsort函数](#2.1 介绍qsort函数)
[2.2 qsort函数排序整型数据](#2.2 qsort函数排序整型数据)
[2.3 qsort函数排序结构体数据](#2.3 qsort函数排序结构体数据)
[3. qsort函数模拟实现](#3. qsort函数模拟实现)
1. 回调函数
1.1 什么是回调函数
--简单说,回调函数就是由一个通过函数指针调用的函数。
--如果把函数的指针(地址)作为参数传给另一个函数,当函数指针被用来调用所指向的函数时,这个被调用的函数就是回调函数。回调函数不由该函数的实现方式直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
--回调函数的一大特点:回调函数简化代码避免重复代码 ------>如果多个函数需要类似的逻辑,回调可以提取公共部分,减少重复。
--就比如上一篇博客分享的:一般方式实现计算器功能的代码,就可以使用回调函数,来看一下吧:
cpp
int Add(int x, int y)//这里的Add就是回调函数
{
return x + y;
}
void test(int(*pf)(int, int))//参数时函数指针,因为要接收函数地址
{
int r = pf(10, 20);
printf("%d\n", r);
}
int main()
{
test(Add);//将Add函数的地址传给test函数
return 0;
}
1.2 回调函数试验:改造一般方式实现计算器功能
--在前面的博客有分享怎么一般方式来实现计算器的基本功能,但是部分代码重复较高,今天就用回调函数来优化:
cpp
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;
}
//函数的参数是函数指针,可以接收不同函数的地址
//接收的地址不同,调用的函数就不同
//这个函数根据参数的不同就能实现不同的功能
void calc(int(*pa)(int, int))
{
int ret = 0;
int x = 0;
int y = 0;
printf("请输入操作数:");
scanf("%d %d", &x, &y);
ret = pa(x, y);
printf("%d\n", ret);
}
void menu()
{
printf("****************************\n");
printf("******1.Add 2.Sub******\n");
printf("******3.Mul 4.Div******\n");
printf("********* 0.exit *********\n");
printf("****************************\n");
}
int main()
{
int input = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
calc(Add);
break;
case 2:
calc(Sub);
break;
case 3:
calc(Mul);
break;
case 4:
calc(Div);
break;
case 0:
printf("已退出计算机程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
--看完这串代码,可以知道:里面的像add、sub、mul、div这四个函数都是回调函数
--从图中可以清楚的看到,add等函数将地址传给calc函数。calc函数的参数是函数指针用来接收地址,在内部调用函数。
--这样就看到了回调函数到底用来做什么:把调用的函数的地址以参数形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数。
2. qsort函数
2.1 介绍qsort函数
-- qsort 是C标准库(<stdli.h>)提供的快速排序(Quick Sort)函数,用于对任意类型的数组进行排序。
--优点:
1. 通用性强(支持任意数据类型): 2. 灵活性高(自定义排序规则):
基本类型(
int
,float
,char
) 升序 / 降序指针类型(
char*
,void*
) 按字符串长度排序结构体(
struct
) 多关键字排序(如先按年龄,再按姓名)动态分配的数组
--qsort函数返回类型、参数:
cpp
void qsort(
void *base, ------>指向要排序的数组首元素的指针。
size_t nmemb, ------>数组元素个数
size_t size, ------>每个元素的字节大小
int (*compar)(const void *, const void *) ------>比较函数指针,用于比较数组中的两个元素
);
--对于函数参数为两个void,可以接收任意类型的指针。
返回值:
<0
:a
应排在b
前面(升序)。
=0
:a
和b
相等。
>0
:a
应排在b
后面(降序)。
2.2 qsort函数排序整型数据
cpp
//升序,cmp_int1用来比较两个整形数据
int cmp_int1(const void* p1, const void* p2)
{
return (*(int*)p1) - (*(int*)p2);
//void*指针不能直接进行解引用,强转为整型指针类型
//默认升序是因为 a - b 的写法符合直观的"从小到大"逻辑
}
//降序
int cmp_int2(const void* p1, const void* p2)
{
return (*(int*)p2) - (*(int*)p1);//反转逻辑
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0;i < sz;i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 3,1,5,8,7,9,2,4,6,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
print_arr(arr, sz);
//使用qsort函数的,需要自己写一个比较函数
qsort(arr, sz, sizeof(arr[0]), cmp_int1);
printf("升序:");
print_arr(arr, sz);
qsort(arr, sz, sizeof(arr[0]), cmp_int2);
printf("降序:");
print_arr(arr, sz);
}
2.3 qsort函数排序结构体数据
cpp
#include<string.h>
#include<stdlib.h>
struct Stu
{
char name[30];
int age;
};
//按年龄比较
int cmp_stu_by_age(const void* p1, const void* p2)
{
return (((struct Stu*)p1)->age - ((struct Stu*)p2)->age);
}
//按名字
int cmp_stu_by_name(const void* e1, const void* e2)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
//strcmp是专门用来比较两个字符串的大小的
}
void print_stu(struct Stu arr[], int sz)
{
int i = 0;
for (i = 0;i < sz;i++)
{
printf("%s:%d\n", arr[i].name, arr[i].age);
}
printf("\n");
}
//按照年龄来排序
void test2()
{
struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
print_stu(arr, sz);
}
//按照名字来排序
void test3()
{
struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
print_stu(arr, sz);
}
int main()
{
test2();
test3();
return 0;
}
3. qsort函数模拟实现
--使用回调函数,模拟实现qsort(采用冒泡的方式)。(首次使用void*类型)
cpp
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}
void _swap(void* p1, void* p2, int size)
{
int i = 0;
for (i = 0; i < size; i++)
{
char tmp = *((char*)p1 + i);
*((char*)p1 + i) = *((char*)p2 + i);
*((char*)p2 + i) = tmp;
}
}
void bubble(void* base, int count, int size, int(*cmp)(void*, void*))
{
int i = 0;
int j = 0;
for (i = 0; i < count - 1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
{
_swap((char*)base + j * size, (char*)base + (j + 1) * size,
size);
}
}
}
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
往期复习:
1. #C语言------学习攻略:深挖指针路线(一)--指针变量、地址、意义与指针运算
2. #C语言------学习攻略:深挖指针路线(二)--const修饰、野指针分析、断言和指针的作用
3. #C语言------学习攻略:深挖指针路线(三)--数组与指针的结合、冒泡排序
4.#C语言------学习攻略:深挖指针路线(四)--字符指针变量,数组指针变量,二维数组传参的本质,函数指针变量,函数指针数组
**结语:本篇内容就到这里了,主要分享了指针变量类型的一些内容,后续仍会分享指针的相关知识;指针的内容需要反复研读 ,如果这篇文章对你的学习有帮助的话,欢迎一起讨论学习,**你这么帅、这么美给个三连吧~~~