目录

1、回调函数

1.1、回调函数的概念

1.2、回调函数的使用

1.2.1、   案例一:

1.2.2、案例二:

1.3、qsort函数

1.3.1、qsort函数介绍

1.3.2、 qsort函数的使用

1.3.3、用冒泡排序思想模拟实现qsort


1、回调函数

1.1、回调函数的概念

回调函数就是一个通过函数指针调用的函数如果你把函数的指针(地址)作为参数传给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

理解:如果拿到一个a函数的地址,通过a函数的地址,再去调用它所指向的函数的时候,被调的那个函数(a函数)就被叫做回调函数。

 1.2、回调函数的使用

   1.2.1、   案例一:

        ​​​​​计算器的模拟实现:

#include<stdio.h>
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 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;int x = 0;int y = 0;int ret = 0; do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:printf("请输入两个操作数:>");//每个case语句中都要写这两句代码这样就使代码冗 //余scanf("%d %d", &x,&y);ret = add(x,y);printf("%d\n",ret);break;case 2:printf("请输入两个操作数:>");scanf("%d %d", &x,&y);ret = sub(x,y);printf("%d\n",ret);break;case 3:printf("请输入两个操作数:>");scanf("%d %d", &x,&y);ret = mul(x,y);printf("%d\n",ret);break;case 4:printf("请输入两个操作数:>");scanf("%d %d", &x,&y);ret = div(x,y);printf("%d\n",ret);break;case 0:printf("退出计算器\n");break:defalut:printf("选择错误\n");break;}} while (input);
}

那么改进这个代码,就现在掌握的知识有两种方法

1、使用函数指针数组来实现

2、使用回调函数

这里使用回调函数来进行改进

#include<stdio.h>
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 menu()
{printf("***********************\n");printf("**** 1.add  2.sub  ****\n");printf("**** 3.mul  4.div  ****\n");printf("**** 0.exit        ****\n");printf("***********************\n");
}
//这里通过重新封装一个函数,来解决代码冗余的问题。
void calc(int (*p)(int,int))//将add、mul、sub、div函数的地址传给calc函数,calc函数使用 //指针p来接收函数地址。
{int x = 0;int y = 0;int ret = 0; scanf("%d %d",&x,&y);ret = p(x,y);//在适当的位置通过函数指针调用加、减、乘、除函数printf("%d\n",ret);
}int main()
{int input = 0;int x = 0;int y = 0;int ret = 0; do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:calc(add);//这里将add函数(函数名相当于函数地址,这里是传址调用)作为参 //数传给calc函数break;case 2:calc(sub);break;case 3:calc(mul);break;case 4:calc(div);break;case 0:printf("退出计算器\n");break:defalut:printf("选择错误\n");break;}} while (input);
}这样的机制就叫做回调函数的机制

 1.2.2、案例二:

先来回忆一下前面说过的冒泡排序,分析一下冒泡排序中存在的问题。

冒泡排序

#include<stdio.h>//bubble_sort函数只能排序整型数据
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(int arr[],int sz)
{int i = 0;for(i = 0;i <sz; i++){printf("%d ",arr[i]);}printf("\n");
}
int main()
{int arr[] = {2,1,3,7,5,9,6,8,0,4};int sz = sizeof(arr)/sizeof(arr[0]);bubble_sort(arr,sz);print(arr,sz);return 0;
}

上述代码的问题是,一但函数写死之后,参数就固定了,就只能排序固定的数据了。

 分析如何用qsort函数来解决冒泡排序中存在的问题

1.3、qsort函数

首先来了解一下qsort函数

qsort函数是C语言标准库提供的排序函数,qsort函数的底层是快速排序的思想。

1.3.1、qsort函数介绍

qsort函数可以排序任意类型的数据

comper是一个指针,指向一个比较函数,这个函数的参数是const void*,const void*

comper的意思为比较

(1)、对qsort函数的分析

        base指针指向待排序数组的第一个元素
        num表示这个数组有几个待排序的元素
        size表示这些待排序的数据的大小,单位(字节)
        compar表示指针指向一个函数,这个函数用来比较两个元素

(2)、对参数void* base的解读

        1>、void*的指针表示为无具体类型的指针,所以void*的指针可以接收任意类型的地址

                由于qsort函数可以排序任意类型的数据,所以用void*作为指针base的指针型。

         2>、但是 void *p = &i;时,不能直接进行解引用操作,

                  可以将指针p强制类型转换成与i相应的指针类型来解引用。

例如:

(3)、对参数int(*comper)(const void*,const void*)进行解读

1>、 由于qsort函数可以对任意类型的数据进行排序,

例如:整型、浮点型、字符串、结构体等。

由于每个类型的比较方式不同,所以比较函数由用户自己编写,将函数地址传给                          qsort函数,所以用指针comper来接收比较函数的地址

2>、返回类型为int

qsort函数规定:

当第一个指针指向的元素<第二个指针指向的元素,返回一个小于0的数子;

当第一个指针指向的元素=第二个指针指向的元素,返回一个等于0的数子;

当第一个指针指向的元素>第二个指针指向的元素,返回一个大于0的数子;

1.3.2、 qsort函数的使用

1、整型类型比较

#include<stdio.h>
#include<stdlib.h>
void print(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)
{if (*(int*)e1 < *(int*)e2)return -1;else if(*(int*)e1 > *(int*)e2)return 1;elsereturn 0;
}//int cmp_int(const void* e1, const void* e2)
//{
//    return(*(int*)e1 - *(int*)e2);
//}可以用这个代码优化前面的比较函数,可以更好的控制排序为降序或升序int main()
{int arr[] = { 2,1,3,7,5,9,6,8,0,4 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);print(arr, sz);return 0;
}

2、结构体排序

1>、按名字排序

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Stu
{char name[20];int age;
};
//按名字比较
int cmp_stu_by_name(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}void print(struct Stu* s, int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%s %d\n", s[i].name, s[i].age);}
}int main()
{struct Stu s[] = { {"zhangsan", 20},{"lisi", 55},{"wangwu", 40} };//按名字比较int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);print(s,sz);return 0;
}

这是按照字符串中字母对应的ASCII码值进行比较

2>、按年龄比较

#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;
}void print(struct Stu* s, int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%s %d\n", s[i].name, s[i].age);}
}int main()
{struct Stu s[] = { {"zhangsan", 20},{"lisi", 55},{"wangwu", 40} };//按名字比较int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);print(s,sz);return 0;
}

结果为:

1.3.3、用冒泡排序思想模拟实现qsort

#include<stdio.h>
void print(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}
void Swap(char* buf1, char* buf2, int width)
{int i = 0;for (i = 0; i < width; i++)//这里用整型数据进行说明,在内存中每个元素占4个字节, //width=4,一个字节一个字节进行交换,{char tmp = *buf1;//这里申请char类型的数据空间,原因是char类型的数据占一个内存空 //间,指针buf指向的内容也占一个字节的内存空间*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(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++){if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)//通过指针cmp来调用函数cmp_int,将两个待比较数据的地址传给cmp_int函数{Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);//Swap函数用来进行比较后的交换,这里交换是一个字节一个字节来交换数据。 //base的类型是void*,强制类型转换成char*,width是数据的宽度, }}}
}
int cmp_int(const void* e1, const void* e2)
{return(*(int*)e1 - *(int*)e2);//这里可以改变数组的排序方式
}void test()
{int arr[] = { 2,1,3,7,5,9,6,8,0,4 };int sz = sizeof(arr) / sizeof(arr[10]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print(arr, sz);
}int main()
{test();return 0;
}

上述代码画图将讲解:

【C语言进阶】指针 下相关推荐

  1. C语言进阶——指针笔试题图解

    作者:敲代码の流川枫 博客主页:流川枫的博客 专栏:C语言从入门到进阶 语录:Stay hungry stay foolish 笔试题1: int main() { int a[5] = { 1, 2 ...

  2. [C语言简明教程] 指针的进阶(下)

    文章目录 前言 一.函数指针 二.函数指针数组 三.回调函数 总结 前言 C语言中指针的重要性是:通过指针不仅可以对数据本身,还可以对存储数据的变量地址进行操作.指针能够帮助我们快速地传递数据,减少内 ...

  3. c语言野指针导致问题,C语言进阶之路(三)----野指针的产生原因及解决办法

    1.会产生野指针的做法 #include //这就是一种错误的写法 int main(){ int *p = NULL; p = (int *)malloc(); //释放P所指向的内存空间,但指针变 ...

  4. 【C语言进阶深度学习记录】三十五 程序中的堆、栈以及静态存储区(数据区)

    学习交流加 个人qq: 1126137994 个人微信: liu1126137994 学习交流资源分享qq群: 962535112 在我之前学习底层的知识的时候,也写过相关的内容.可以对比的学习:[软 ...

  5. 【C语言进阶深度学习记录】二十六 C语言中的字符串与字符数组的详细分析

    之前有一篇文章是学习了字符和字符串的,可以与之结合学习:[C语言进阶深度学习记录]十二 C语言中的:字符和字符串 文章目录 1 字符串的概念 1.1 字符串与字符数组 1.2 字符数组与字符串代码分析 ...

  6. Go语言基本语法 (下)

    原作者博客链接:[https://www.liwenzhou.com/][https://www.liwenzhou.com/] Golang 中文文档地址:https://studygolang.c ...

  7. Go语言进阶,结构体与json字符串格式的互相转换

    对于结构体大家都很熟悉,是一种自定义类型,可以将不同类型的同属于这个类的属性(成员变量)集合在一起,换句话说这些成员变量属于键值对,那么这种也就是常见的json格式,我们来看下如何将结构体转成json ...

  8. C语言 | 进阶之路第一关

    目录 前言 - 文章概述 深入剖析数据在内存中的存储 数据的类型 整型在内存中的存储 浮点型在内存中的存储 指针进阶 字符指针 指针数组 数组指针 数组传参和指针传参 函数指针 函数指针数组 指向函数 ...

  9. 一道指针压轴笔试题讲解 (进阶指针必看)

    今天我来给大家讲解一道指针压轴笔试题,同时也是进阶指针知识必会的一道题.相信大家在认真梳理清楚这道题后,对指针的知识了解定会更上一层楼 ~ 话不多说,上题目 : 请问程序输出结果是什么 ? 相信大家一 ...

  10. 理解C语言中指针的声明以及复杂声明的语法

    昨天刚把<C程序设计语言>中"指针与数组"章节读完,终于把心中的疑惑彻底解开了.现在记录下我对指针声明的理解,顺便说下如何在C语言中创建复杂声明以及读懂复杂声明. 本文 ...

最新文章

  1. day3 集合、文件操作、函数、局部变量
  2. 10.11杭州Clouder lab 十分钟搭建共享应用 2:如何通过日志服务实现用户的日志收集与分析...
  3. Kruskal算法的C语言程序
  4. 利用html的header下载文件
  5. FreeSql (三十三)CodeFirst 类型映射
  6. Java缓存框架使用EhCache结合Spring AOP
  7. python启动方法_python进程开启的两种方式
  8. ar android app,RakugakiAR安卓版
  9. Tensorflow学习—— 预创建的 Estimator
  10. MySQL里的日期技巧
  11. 硬件开源为什么如此之难?
  12. 如何在Vue项目中使用vw实现移动端适配
  13. php 发送图片,php+curl 发送图片处理代码分享
  14. TSAP(2) : 时区切换
  15. 快逸报表4.0 分组处理
  16. flex + tomcat + myEclipse环境配置与使用(四)
  17. 4种方法解决js跨域的实现方式
  18. Vue 开源项目库汇总
  19. 更精确的新旧中国居民身份证号码验证算法
  20. form表单同时提交带文本和图片的数据

热门文章

  1. python爬取大众点评_Python爬虫,获取大众点评上海地区的餐饮信息!
  2. 化作太行山谷槐花香——谨以此文纪念我的姐妹Jessy
  3. 网络性能监控工具Smokeping
  4. 7-3 判断闰年及星期几 (20 分)
  5. 利用Python输出九九乘法表
  6. 未来5年,6大风口行业
  7. C语言-----计算1-1/2+1/3-1/4+....+1/99-1/100+....直到最后一项的绝对值小于10-4为止
  8. J2EE高级软件工程师面试题集
  9. LaTeX——命令注释
  10. ATF快速扫盲(Quick Start)