1、回调函数
回调函数就是⼀个通过函数指针调⽤的函数。函数参数的形式为函数指针类型。
当你把函数/函数的地址作为参数传递给相应函数是,如果这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用的,而是再特定的条件或输入操作下有另外的一方调用的,用于对该操作或条件的相应。
结合上节课的计算器实例,进行如下修改可得:
#include <stdio.h>
void menu()
{
printf(" 0、exit \n");
printf(" 1、Add \n");
printf(" 2、Sub \n");
printf(" 3、Mul \n");
printf(" 4、Div \n");
}
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(*pf)(int x, int y))
{
int x = 0;
int y = 0;
printf("请输入两个操作数:");
scanf("%d%d", &x, &y);
int ret = pf(x, y);
printf("%d\n", ret);
}
int main()
{
int input = 0;
int x, y;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch(input)
{
case 0:
printf("退出计算器\n");
break;
case 1:
calc(Add);
break;
case 2:
calc(Sub);
break;
case 3:
calc(Mul);
break;
case 4:
calc(Div);
break;
default:
printf("输入错误,请重新输入!\n");
break;
}
} while (input);
return 0;
}
综上:回调函数同样可以使代码更加简洁,这样的好处都是函数指针的应用所带来的。
qsort函数的使用
c++官网给出的函数介绍:
函数原型:
void qsort (void* base, size_t num, size_t size,int (*compar)(const void*, const void*));
void qsort (void* base ---- 元素的起始地址
, size_t num ----- 元素个数
, size_t size ----- 元素的字节大小
,int (*compar)(const void*, const void*)) --- 比较大小的函数
使⽤qsort函数排序整型数据:
#include <stdlib.h>
#include <stdio.h>
int cmp_int(const void* p1, const void* p2)
{
return (*(int*)p1) - (*(int*)p2);
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[] = { 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);
print_arr(arr,sz);
return 0;
}
使⽤qsort排序结构数据:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stu//结构体的创建
{
char name[20];
int age;
};
int cmp_by_name(const void* buf1,const void* buf2)//按姓名排序
{
return strcmp(((struct stu*)buf1)->name, ((struct stu*)buf2)->name);
}
int cmp_by_age(const void* buf1, const void* buf2)//按年龄排序
{
return (((struct stu*)buf1)->age) - (((struct stu*)buf2)->age);
}
void print_struct(struct stu s[], int sz)//打印结构体内容
{
for (int i = 0; i < sz; i++)
{
printf("%s %d\n", s[i].name, s[i].age);
}
}
void test1()
{
struct stu s[] = { { "zhangsan",33 }, { "lisi", 25 },{ "likezhen", 21 },{"zoutianlei",19}};
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_by_name);
print_struct(s,sz);
}
void test2()
{
struct stu s[] = { { "zhangsan",33 }, { "lisi", 25 },{ "likezhen", 21 },{"zoutianlei",19} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_by_age);
print_struct(s, sz);
}
int main1()
{
//test1();
//test2();
return 0;
}
这里要注意的是,如果需要排序的内容是整型,可以直接返回两个元素相减的结果,但是要看清谁见谁,避免排反序;而如果排序的内容是字符串,可以使用strcmp函数来比较,大于零返回整数,小于等于零不进行操作。
冒泡函数与qsort的区别
先来说一下冒泡函数:
#include <stdio.h>
//冒泡排序
void 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 tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
void print_arr(int arr[], int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
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_arr(arr, sz);
return 0;
}
使用冒泡函数排序数组中的内容是比较常用的方法,但是冒泡排序只能排序整型数据,所以有一定的不足。而qsort可以对任意类型的数据进行排序,包括结构体。
下面我们进行qsort函数的模拟实现,去探究函数内部的逻辑思维:
#include<stdio.h>
int cmp_int(const void* buf1, const void* buf2)//这里只是举了一个整型的例子,如果需要排序字符串的话,用strcmp函数即可。
{
return (*(int*)buf1) - (*(int*)buf2);
}
void Swap(const void* base1, const void* base2, size_t width)//交换
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *((char*)base1 + i);
*((char*)base1 + i) = *((char*)base2 + i);
*((char*)base2 + i) = tmp;
}
}
void bubble_sort(void* base,size_t sz, size_t width, int(*cmp)(const void* buf1,const void* buf2))//模拟函数,这里的函数指针是本函数的关键
{
int i = 0;
for (i = 0; i < sz - 1; i++)//趟数
{
int j = 0;
for (j = 0; j < sz - 1 - i; j++)//一趟的交换次数
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
{
Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
}
void print_arr(int arr[], int sz)//打印
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
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,sizeof(arr[0]),cmp_int);
print_arr(arr, sz);
return 0;
}
部分拆解:
void Swap(const void* base1, const void* base2, size_t width)//交换
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *((char*)base1 + i);
*((char*)base1 + i) = *((char*)base2 + i);
*((char*)base2 + i) = tmp;
}
}这里还有一种写法:
void Swap(char* base1, char* base2, size_t width)//交换
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *base1;
*base1 = *base2;
*base2 = tmp;
base1++;
base2++;
}
}两种方式的目的都是将base1和base2拆解成一个字节的数据进行多次交换。
这样就避免了int型或更长整型倍数不符的问题。
void bubble_sort(void* base,size_t sz, size_t width, int(*cmp)(const void* buf1,const void* buf2))//模拟函数,这里的函数指针是本函数的关键
{
int i = 0;
for (i = 0; i < sz - 1; i++)//趟数
{
int j = 0;
for (j = 0; j < sz - 1 - i; j++)//一趟的交换次数
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
{
Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
}将base强制转换为char*类型,可以是函数接收任何类型的数据通过单个元素的字节大小width都够准确找到下以个元素的起始位置。
本段代码需要细细品尝并进行模仿,慢慢体会其中的细节,掌握之后会提升很多。