目录
1. 回调函数是什么?
回调函数就是⼀个通过函数指针调⽤的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数 时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条 件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。
我们来利用回调函数来写一个简化的计算器
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
void calc( int(*pf)(int, int) )
{
int x = 0;
int y = 0;
int ret = 0;
printf("请输入数字:");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("结果为:%d\n",ret);
}
int main()
{
int input = 0;
do
{
printf("***********************\n");
printf(" 1.add 2.sub \n");
printf(" 3.mul 4.div \n");
printf(" 0.exit \n");
printf("请选择:\n");
scanf("%d", &input);
switch (input)
{
case 1:
calc(add); // ----calc中文意思是:计算
break;
case 2:
calc(sub);
break;
case 3:
calc(mul);
break;
case 4:
calc(div);
break;
case 0:
printf("退出计算机\n");
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
我们可以把调⽤的函数的地址以参数的形式 传递过去,使⽤函数指针接收,函数指针指向什么函数就调⽤什么函数,这⾥其实使⽤的就是回调函 数的功能
解析上面的代码:
下面的代码就是完成两个数的简单的加减乘除
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
这个代码就是我们创建的一个简单的计算器的按键面板
printf("***********************\n");
printf(" 1.add 2.sub \n");
printf(" 3.mul 4.div \n");
printf(" 0.exit \n");
这个代码是利用switch语句来把我们的按键归为一个情况,1就是加入加号的代码里面去,后面的是一个意思,这里的default是输入了不是case里面的数字,而是输入了其他的,那么就会执行整个代码,即打印出‘’选择错误‘’这四个字
switch (input)
{
case 1:
calc(add); // ----calc中文意思是:计算
break;
case 2:
calc(sub);
break;
case 3:
calc(mul);
break;
case 4:
calc(div);
break;
case 0:
printf("退出计算机\n");
default:
printf("选择错误\n");
break;
这里代码意思是:调用calc(add),把add函数的地址传递给calc,然后去执行calc那里面的代码,这个就是回调函数的用法
后面的加减乘除是一样的,这里我就那+来解析
calc(add);
这里的代码意思是:函数 int(*pf)(int, int) 表示pf是一个指向函数的指针,该函数接受两个int 参数并且返回給int
执行流程:选择+的情况,调用了calc(add)把add函数的地址传给calc ,然后calc函数通过pf(x,y)实际上调用的是add(x,y)
void calc( int(*pf)(int, int) )
{
int x = 0;
int y = 0;
int ret = 0;
printf("请输入数字:");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("结果为:%d\n",ret);
}
2.qsort使⽤举例
2.1使⽤qsort函数排序整型数据
qsort中文意思是:快速排序
先使用冒泡排序来排序一组元素---升序
void bubble_sort(int arr[], int sz)
{
//趟数
int i = 0;
for (i = 0; i < sz - 1; i++)//假如有10个元素,一趟循坏我们只要走9次就行了,因为最后一个数字不需要在进行比较了
{
//一趟内部数字的两两比较
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1]) //如果前一项大于后一项则进入if语句
{
//前后比较,大的一项与小的一项进行换位置
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
void print_sort(int arr[],int sz)
{
int i = 0;
for (i=0;i<sz;i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]); //计算数组元素个数
bubble_sort(arr, sz); //调用函数,完成元素的升序
print_sort(arr, sz); //调用函数,打印排序后的元素
return 0;
}
输出结果:

解析上面的代码:
这里的 int sz = sizeof(arr) / sizeof(arr[0]); 的作用是计算数组元素的个数
bubble_sort(arr, sz); //调用函数,完成元素的升序
print_sort(arr, sz); //调用函数,打印排序后的元素
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]); //计算数组元素个数
bubble_sort(arr, sz); //调用函数,完成元素的升序
print_sort(arr, sz); //调用函数,打印排序后的元素
return 0;
}
这里的代码意思是:利用print_sort函数以及for循环,将我们已经排序好的元素全部打印出来
void print_sort(int arr[],int sz)
{
int i = 0;
for (i=0;i<sz;i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
这里的代码意思是:
if (arr[j] > arr[j + 1]) ---->如果前一项大于后一项则进入if语句
int tmp = arr[j];
arr[j] = arr[j + 1]; ----->前后比较,大的一项与小的一项进行换位置
arr[j + 1] = tmp;
void bubble_sort(int arr[], int sz)
{
//趟数
int i = 0;
for (i = 0; i < sz - 1; i++)//假如有10个元素,一趟循坏我们只要走9次就行了,因为最后一个数字不需要在进行比较了
{
//一趟内部数字的两两比较
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1]) //如果前一项大于后一项则进入if语句
{
//前后比较,大的一项与小的一项进行换位置
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
代码解析:假如有10个元素,一趟循坏我们只要走9次就行了,因为最后一个数字不需要在进行比较了
for (i = 0; i < sz - 1; i++)
利用qsort来进行升序(繁琐版)
int cmp_int(const void* p1, const void* p2) //cmp_int 中文意思是:‘整型比较’
{
if (*(int*)p1 > *(int*)p2) //(int*)---将p1,p2强行转换为int*
{
return 1; //两个数进行比较,如果前面的数大于后面一个数就换位置,返回数大于0,这里我们写1
}
else if (*(int*)p1 == *(int*)p2)
{
return 0; //两个数比较,如果相等,返回数为0
}
else
{
return -1; //与第一个相反,如果小于,则不用换位置,返回数小于0
}
}
void print_arr(int arr[], int sz) //循环打印数组里面的元素
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void test()
{
int arr[] = { 2,3,6,7,9,4,0,1,5,8 };
int sz = sizeof(arr) / sizeof(arr[0]); //计算数组的元素个数
qsort(arr, sz, sizeof(arr[0]), cmp_int); //qsort函数的调用,还需要头文件 stdlib.h
print_arr(arr, sz); //将排序好的元素打印出来
}
int main()
{
test(); //写一段使用qsort函数的代码,来完成升序
return 0;
}
输出结果:

解析代码:
void test()
{
int arr[] = { 2,3,6,7,9,4,0,1,5,8 };
int sz = sizeof(arr) / sizeof(arr[0]); //计算数组的元素个数
qsort(arr, sz, sizeof(arr[0]), cmp_int); //qsort函数的调用,还需要头文件 stdlib.h
print_arr(arr, sz); //将排序好的元素打印出来
}
使用qsort函数的前提是需要包含一个头文件的,即<stdlib.h>
qsort 函数的标准格式
void qsort(void *base, size_t num, size_t size, int (*compar)(const void*, const void*));
参数说明:
-
base:指向要排序的数组的第一个元素的指针 -
num:数组中元素的个数 -
size:每个元素的大小(字节数) -
compar:比较函数的指针
现在我们可以好好解析一下qsort函数的使用了
qsort(arr, sz, sizeof(arr[0]), cmp_int);
参数对应:
-
arr→void* base -
sz→size_t num// sz 是 size_t 类型 -
sizeof(arr[0])→size_t size// sizeof 返回 size_t 类型 -
cmp_int→ 函数指针参数 // 比较函数的地址
现在我们来解析下这个代码:
int cmp_int(const void* p1, const void* p2) //cmp_int 中文意思是:‘整型比较’
{
if (*(int*)p1 > *(int*)p2) //(int*)---将p1,p2强行转换为int*
{
return 1; //两个数进行比较,如果前面的数大于后面一个数就换位置,返回数大于0,这里我们写1
}
else if (*(int*)p1 == *(int*)p2)
{
return 0; //两个数比较,如果相等,返回数为0
}
else
{
return -1; //与第一个相反,如果小于,则不用换位置,返回数小于0
}
}
解析:
-
(int*)p1:将void*指针转换为int*指针
-
*(int*)p1:解引用,获取实际的整数值
两个数进行比较,如果前面的数大于后面一个数就换位置,返回数大于0,这里我们写1
return 1;
return 0;
return -1
现在我们来使用简化版的代码,将上面的代码全部简化
利用qsort来进行升序(简化版)
int cmp_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2; //如果需要降序,只需将p1和p2换个位置就行了
}
void print_arr(int arr[], int sz) //循环打印数组里面的元素
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void test()
{
int arr[] = { 2,3,6,7,9,4,0,1,5,8 };
int sz = sizeof(arr) / sizeof(arr[0]); //计算数组的元素个数
qsort(arr, sz, sizeof(arr[0]), cmp_int); //qsort函数的调用,还需要头文件 stdlib.h
print_arr(arr, sz); //将排序好的元素打印出来
}
int main()
{
test(); //写一段使用qsort函数的代码,来完成升序
return 0;
}
上面的代码只改了这一串函数代码:
int cmp_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2; //如果需要降序,只需将p1和p2换个位置就行了
}
比较函数的返回值规则
-
返回负数:p1 应该排在 p2 前面
-
返回0:p1 和 p2 相等
-
返回正数:p1 应该排在 p2 后面
这就是为什么 return (*(int*)p1 - *(int*)p2) 能实现升序排序的原因!
2.2 使⽤qsort排序结构数据
这里我们先回顾一下结构体的打印
struct Stu
{
char name[20];
int age;
};
int main()
{
struct Stu s = { "zhangsan",18 };
printf("%s %d \n", s.name, s.age);
return 0;
}
输出结果:

利用指针来进行结构体的打印
struct Stu
{
char name[20];
int age;
};
void print(struct Stu* ps)
{
//printf("%s %d \n", (*ps).name, (*ps).age); //利用解引用打印
printf("%s %d \n", ps->name, ps->age); //利用结构体成员访问符打印 ,这里的->是结构体成员的间接访问操作符
} 用法: 结构体指针->成员名
int main()
{
struct Stu s = { "zhangsan",20 };
print(&s);
return 0;
}
输出结果:

这里的printf我们有两个打印的方法:
- printf("%s %d \n", (*ps).name, (*ps).age); //利用解引用打印
- printf("%s %d \n", ps->name, ps->age); //利用结构体成员访问符打印 ,这里的->是结构体成员的间接访问操作符
用法: 结构体指针->成员名
利用qsort函数排序结构体数据
按名字来比较
按照名字来排序,即字符串的比较
struct Stu //结构体的创建
{
char name[20];
int age;
};
int cmp_stu_by_name(const void* p1, const void* p2) //按照名字来排序,即字符串的比较
{
return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name); //名字的比较,这里的 ->是结构体成员间接访问操作符
} //由于p1 p2返回类型是void*,所以这里我们需要强行把它们转换为结构体指针
void test2()
{
struct Stu arr[3] = { {"zhangsan",20},{"lisi",18} ,{"wangwu",35} };//结构体的初始化
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);//名字比较--字符串比较--需要使用strcmp函数进行比较,它需要一个头文件string.h
}
int main()
{
test2(); //写一段使用qsort函数的代码,来进行结构体的排序
return 0;
}
调试结果:

按年龄来比较
按年龄来比较
struct Stu //结构体的创建
{
char name[20];
int age;
};
int cmp_stu_by_age(const void* p1, const void* p2) //按照名字来排序,即字符串的比较
{
return ((struct Stu*)p1)->age- ((struct Stu*)p2)->age;
}
void test2()
{
struct Stu arr[3] = { {"zhangsan",20},{"lisi",18} ,{"wangwu",35} };//结构体的初始化
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);//名字比较--字符串比较--需要使用strcmp函数进行比较,它需要一个头文件string.h
}
int main()
{
test2(); //写一段使用qsort函数的代码,来进行结构体的排序
return 0;
}
调试结果:

解析代码:
定义了一个学生结构体,包含姓名和年龄两个成员
struct Stu //结构体的创建
{
char name[20];
int age;
};
比较函数 cmp_stu_by_age
-
const void* p1, const void* p2:qsort 要求的参数格式 -
(struct Stu*)p1:将 void 指针转换为 struct Stu 指针 -
((struct Stu*)p1)->age:访问结构体的 age 成员 -
return ... - ...:返回年龄差值,实现升序排序
int cmp_stu_by_age(const void* p1, const void* p2) //按照名字来排序,即字符串的比较
{
return ((struct Stu*)p1)->age- ((struct Stu*)p2)->age;
}
测试函数 test2
- 创建结构体数组初始化:
- 计算数组大小:
- 调用 qsort:
void test2()
{
struct Stu arr[3] = { {"zhangsan",20},{"lisi",18} ,{"wangwu",35} };//结构体的初始化
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);//名字比较--字符串比较--需要使用strcmp函数进行比较,它需要一个头文件string.h
}
3. qsort函数的模拟实现
使⽤回调函数,模拟实现qsort(采⽤冒泡的⽅式)
完整代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
}
一一解析:
比较函数 int_cmp
int int_cmp(const void * p1, const void * p2)
{
return (*( int *)p1 - *(int *) p2);
}
知识点:
-
const void *:通用指针,可以指向任何数据类型 -
(int *)p1:将void指针强制转换为int指针 -
*(int *)p1:解引用获取整数值 -
返回值>0:p1>p2,需要交换
-
返回值=0:p1=p2,相等
-
返回值<0:p1<p2,保持原位
交换函数 _swap
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;
}
}
核心知识点:
-
(char *)p1:转换为字节指针,便于按字节操作 -
*((char *)p1 + i):访问第i个字节的内容 -
通过循环逐个字节交换,实现任意数据类型的交换
冒泡排序函数 bubble
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);
}
}
}
}
关键知识点:
地址计算
(char *) base + j * size
-
(char *)base:转换为字节指针 -
j * size:计算第j个元素的偏移量 -
示例:int数组,size=4,j=2 → 偏移8字节
回调函数调用:
cmp((char *) base + j*size, (char *)base + (j + 1)*size)
-
动态调用用户提供的比较函数
-
实现通用排序算法的关键
int(*cmp)(void *, void *)
// ↑ ↑ ↑ ↑
// 返回 指针 参数1 参数2
// 类型 变量名 类型 类型
-
int(* ):表示这是一个指向返回int的函数的指针 -
cmp:函数指针变量的名称 -
(void *, void *):该函数接受两个void指针参数
通用地址计算机制
(char *) base + j * size
支持任意数据类型的原理
// 对于int数组:size = 4
(char *)base + j * 4
// 对于double数组:size = 8
(char *)base + j * 8
// 对于结构体:size = sizeof(struct)
(char *)base + j * sizeof(struct Student)
主函数 main
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);
// ... 输出代码
return 0;
}
函数调用:
bubble(arr, 10, 4, int_cmp);
// 参数:数组首地址, 元素个数, 元素大小, 比较函数
以上就是我们的全部内容了
3288

被折叠的 条评论
为什么被折叠?



