三、指向函数指针数组的指针
0x00 函数指针数组的指针定义
📚 定义:指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素是函数指针。
0x01 函数指针数组的例子
💬 ppfArr 就是一个函数指针数组:
int Add(int x, int y) { return x + y; } int main() { int arr[10] = {0}; int (*p)[10] = &arr; // 取出数组的地址 int (*pfArr[4])(int, int); // pfArr是一个数组 - 函数指针的数组 // ppfArr是一个指向[函数指针数组]的指针 int (* (*ppfArr)[4])(int, int) = &pfArr; // ppfArr 是一个数组指针,指针指向的数组有4个元素 // 指向的数组的每个元素的类型是一个函数指针 int(*)(int, int) return 0; }
0x02 指针的总结
🔺 指针:
四、回调函数(call back)
0x00 回调函数的概念
回调函数是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时候,我们就称之为回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的时间或条件发生时由另外的一方调用的,用于该事件或条件进行响应。
0x01 回调函数的例子
💬 用刚才的 switch 版本的计算器为例:
#include <stdio.h> void menu() { printf("*****************************\n"); printf("** 1. add 2. sub **\n"); printf("** 3. mul 4. div **\n"); printf("** 0. exit **\n"); printf("*****************************\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, int)) { int x = 0; int y = 0; printf("请输入2个操作数:>"); scanf("%d %d", &x, &y); printf("%d\n", pf(x, y)); } 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; }
🔑 解析:上面的代码做到了想要做什么计算就做什么计算的目的,这就是函数指针能够做到的事。一个 Calc 函数就可以做很多的功能,给它传递不同的参数,它就可以做不同的事情。
0x02 无指针类型 void*
void*
0x03 qsort 函数
📚 说明:qsort 函数是C语言编译器函数库自带的排序函数( 需引入头文件 stdlib.h )
💬 回顾冒泡排序:
【维生素C语言】第四章 - 数组 ( 3 - 0x01 )
#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) { int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("\n"); } int main() { int arr[10] = {9,8,7,6,5,4,3,2,1,0}; int sz = sizeof(arr) / sizeof(arr[0]); print_arr(arr, sz); bubble_sort(arr, sz); print_arr(arr, sz); return 0; }
❓ 问题点:我们自己实现的冒泡排序函数只能排序整型顺序,如果我们要排序字符串或者一个结构体,我们是不是要单独重新实现这个函数呢?而 qsort 函数可以帮我们排任意想排的数据类型。
📚 qsort 函数的四个参数:
💬 qsort 整型数据排序(升序):
#include <stdio.h> #include <stdlib.h> /* void qsort ( void* base, size_t num, size_t size, int (*cmp_int)(const void* e1, const void* e2) ); */ int cmp_int(const void* e1, const void* e2) { // 升序: e1 - e2 return *(int*)e1 - *(int*)e2; } void print_arr(int arr[], int sz) { int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("\n"); } void int_sort() { 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); } int main() { int_sort(); return 0; }
🚩 0 1 2 3 4 5 6 7 8 9
❓ 如果我想测试一个结构体数据呢?
💬 那我们就写结构体的cmp函数(升序):
( 需求:结构体内容为 " 姓名 + 年龄 ",使用qsort,实现按年龄排序和按姓名排序 )
#include <stdio.h> #include <stdlib.h> #include <string.h> struct Stu { char name[20]; int age; }; /* void qsort ( void* base, size_t num, size_t size, int (*cmp_int)(const void* e1, const void* e2) ); */ int cmp_struct_age(const void* e1, const void* e2) { return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age; } int cmp_struct_name(const void* e1, const void* e2) { return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name); } void struct_sort() { // 使用qsort函数排序结构体数据 struct Stu s[3] = { {"Ashe", 39}, {"Hanzo", 38}, {"Ana", 60} }; int sz = sizeof(s) / sizeof(s[0]); // 按照年龄排序 qsort(s, sz, sizeof(s[0]), cmp_struct_age); // 按照名字来排序 qsort(s, sz, sizeof(s[0]), cmp_struct_name); } int main() { struct_sort(); return 0; }
🔑 解析:按照年龄排序则比较年龄的大小,按照名字排序本质上是比较Ascii码的大小。
❓ 现在是升序,如果我想实现降序呢?
💡 很简单,只需要把 e1 - e2 换为 e2 - e1 即可:
int cmp_int(const void* e1, const void* e2) { // 降序: e2 - e1 return( *(int*)e2 - *(int*)e1 ); } int cmp_struct_age(const void* e1, const void* e2) { return( ((struct Stu*)e2)->age - ((struct Stu*)e1)->age ); } int cmp_struct_name(const void* e1, const void* e2) { return( strcmp(((struct Stu*)e2)->name, ((struct Stu*)e1)->name) ); }
0x04 模拟实现 qsort 函数
📚 模仿 qsort 实现一个冒泡排序的通用算法
💬 完整代码(升序):
#include <stdio.h> #include <string.h> struct Stu { char name[20]; char age; }; // 模仿qsort实现一个冒泡排序的通用算法 void Swap(char*buf1, char*buf2, int width) { int i = 0; for(i=0; i<width; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; } } void bubble_sort_q ( void* base, // 首元素地址 int sz, // 元素总个数 int width, // 每个元素的大小 int (*cmp)(const void*e1, const void*e2) // 两个元素的函数 ) { // 确认趟数 int i = 0; for(i=0; i<sz-1; i++) { // 一趟排序 int j = 0; for(j=0; j<sz-1-i; j++) { // 两个元素比较 arr[i] arr[j+i] if(cmp( (char*)base+j*width, (char*)base+(j+1)*width ) > 0) { //交换 Swap((char*)base+j*width, (char*)base+(j+1)*width, width); } } } } int cmp_struct_age(const void* e1, const void* e2) { return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age; } int cmp_struct_name(const void* e1, const void* e2) { return strcmp( ((struct Stu*)e1)->name, ((struct Stu*)e2)->name ); } void struct_sort() { // 使用qsort排序结构体数据 struct Stu s[] = {"Ashe", 39, "Hanzo", 38, "Ana", 60}; int sz = sizeof(s) / sizeof(s[0]); // 按照年龄排序 bubble_sort_q(s, sz, sizeof(s[0]), cmp_struct_age); // 按照名字排序 bubble_sort_q(s, sz, sizeof(s[0]), cmp_struct_name); } void print_arr(int arr[], int sz) { int i = 0; for(i=0; i<sz; i++) { printf("%d ", arr[i]); } printf("\n"); } int cmp_int(const void* e1, const void* e2) { // 升序: e1 - e2 return *(int*)e1 - *(int*)e2; } void int_sort() { int arr[] = {9,8,7,6,5,4,3,2,1,0}; int sz = sizeof(arr) / sizeof(arr[0]); // 排序 bubble_sort_q(arr, sz, sizeof(arr[0]), cmp_int); // 打印 print_arr(arr, sz); } int main() { int_sort(); // struct_sort(); return 0; }
🚩 0 1 2 3 4 5 6 7 8 9