C语言的灵魂—指针(四)

📅 发布时间:2026/7/5 7:26:01 👁️ 浏览次数:
C语言的灵魂—指针(四)
一、回调函数回调函数就是一个通过指针调用的函数。如果你把函数的指针地址作为参数传给另一个函数当这个指针被用来调用其所指的函数时被调用的函数就是回调函数。通过回调函数我们可以处理代码中冗余的部分使得代码更加精炼美观。如下所示#include stdio.h 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 ret 0; int x, y; printf(输入操作数: ); scanf(%d %d, x, y); ret pf(x, y); printf(ret %d\n, ret); } int main() { int input 1; do { printf(************************\n); printf( 1:add\n); printf( 2:sub\n); printf( 3:mul\n); printf( 4:div\n); printf(************************\n); 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函数来接收各类计算寒暑当我们需要调用时只需要给它传对应的函数即可二、qsort函数qsort函数是C语言标准库的快速排序函数可以帮助你把数据排序但是他不知道你要怎么比大小所以需要你自己写一个“比较函数”它不同于我们已知的冒泡排序函数它可排列的数据类型不仅仅局限于整型还包含字符型结构体类型等等全由你自己提供的函数决定void qsort(void* base , size_t mem , size_t size , int(* compare)(const void* , const void*))第一个参数指向的是 要排序数组的起始地址第二个参数是 数组的元素个数第三个参数是是 一个元素所占的字节数第四个参数是是 你自己提供的比较函数这个比较函数应该返回一个整型使用示例排列整型#include stdio.h #include stdlib.h // qsort 函数需要包含这个头文件 // qsort函数的使用者得实现一个比较函数 int int_cmp(const void *p1, const void *p2) { return (*(int *)p1 - *(int *)p2); } int main() { int arr[] { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 }; int i 0; qsort(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; }注意为了让这个比较函数可以接受任意类型的变量我们需要把它的参数设置成void*类型但void*是无法进行解引用的因此我们在解引用时需要再把它强制类型转化成原来的类型除了整型我们还可以用它来排列结构体类型#include stdio.h #include stdlib.h #include string.h struct Stu //先创建一个存放学生信息的结构体类型 { char name[20];//名字 int age;//年龄 }; //创建比较年龄的函数 int cmp_stu_by_age(const void* e1, const void* e2) { return ((struct Stu*)e1)-age - ((struct Stu*)e2)-age; } //创建比较名字的函数 int cmp_stu_by_name(const void* e1, const void* e2) { return strcmp(((struct Stu*)e1)-name, ((struct Stu*)e2)-name); } void test2()//排列年龄 { struct Stu s[] { {zhangsan, 20}, {lisi, 30}, {wangwu, 15} }; int sz sizeof(s) / sizeof(s[0]); qsort(s, sz, sizeof(s[0]), cmp_stu_by_age); } void test3()//排列名字 { struct Stu s[] { {zhangsan, 20}, {lisi, 30}, {wangwu, 15} }; int sz sizeof(s) / sizeof(s[0]); qsort(s, sz, sizeof(s[0]), cmp_stu_by_name); } int main() { test2(); test3(); return 0; }现在我们尝试模拟实现它我们可以借鉴下冒泡排序的思路在它的基础上进行改造从而使其不仅仅只能排列整型还可以排列结构体类型#include string.h struct stu { int age; char name[20]; }; int age_cmp(const void* p1 , const void* p2) { return (*(struct stu*)p1).age - (*(struct stu*)p2).age; } int name_cmp(const void* p1, const void* p2) { return strcmp((*(struct stu*)p1).name , (*(struct stu*)p2).name); } void my_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 my_qsort(void* base, int num, int sz , int(* cmp)(void* , void*) ) { int i 0; for (i 0 ; i num-1 ; i) { int j 0; for (j 0 ; j num - 1 - i ; j) { if(cmp((char*)base j*sz , (char*)base (j1)*sz) 0) my_swap((char*)base j * sz, (char*)base (j 1) * sz,sz); } } } int main() { struct stu a { 25,zhangsan }; struct stu b { 27,lisi }; struct stu c { 34,wangwu }; struct stu arr[] {a , b, c}; int i 0; my_qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), age_cmp); for(i 0 ; i (sizeof(arr) / sizeof(arr[0])) ; i) { printf(%d\n, arr[i].age); } my_qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), name_cmp); for (i 0; i (sizeof(arr) / sizeof(arr[0])); i) { printf(%s\n, arr[i].name); } return 0; }这里的难点在于在冒泡函数中我们可以使用数组的形式对数据进行比较大小和交换顺序但是void*类型的数据不支持数组下标运算。因此我们需要转换思路返璞归真。数组运算的本质就是地址加偏移量比如arr[4]就是 *arr 4我们既然有数组的地址单个元素的大小就可以模仿这种形式用地址 偏移量来表示元素因此就有了char*base j*sz这个形式。先将指针强制类型转化成char*类型这样字节的最小偏移单位就是1了再用元素地址加一个元素的偏移量的形式找到相邻下个元素的地址从而达到向cmp函数传参的目的。