【C语言进阶】指针 下
目录
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语言进阶】指针 下相关推荐
- C语言进阶——指针笔试题图解
作者:敲代码の流川枫 博客主页:流川枫的博客 专栏:C语言从入门到进阶 语录:Stay hungry stay foolish 笔试题1: int main() { int a[5] = { 1, 2 ...
- [C语言简明教程] 指针的进阶(下)
文章目录 前言 一.函数指针 二.函数指针数组 三.回调函数 总结 前言 C语言中指针的重要性是:通过指针不仅可以对数据本身,还可以对存储数据的变量地址进行操作.指针能够帮助我们快速地传递数据,减少内 ...
- c语言野指针导致问题,C语言进阶之路(三)----野指针的产生原因及解决办法
1.会产生野指针的做法 #include //这就是一种错误的写法 int main(){ int *p = NULL; p = (int *)malloc(); //释放P所指向的内存空间,但指针变 ...
- 【C语言进阶深度学习记录】三十五 程序中的堆、栈以及静态存储区(数据区)
学习交流加 个人qq: 1126137994 个人微信: liu1126137994 学习交流资源分享qq群: 962535112 在我之前学习底层的知识的时候,也写过相关的内容.可以对比的学习:[软 ...
- 【C语言进阶深度学习记录】二十六 C语言中的字符串与字符数组的详细分析
之前有一篇文章是学习了字符和字符串的,可以与之结合学习:[C语言进阶深度学习记录]十二 C语言中的:字符和字符串 文章目录 1 字符串的概念 1.1 字符串与字符数组 1.2 字符数组与字符串代码分析 ...
- Go语言基本语法 (下)
原作者博客链接:[https://www.liwenzhou.com/][https://www.liwenzhou.com/] Golang 中文文档地址:https://studygolang.c ...
- Go语言进阶,结构体与json字符串格式的互相转换
对于结构体大家都很熟悉,是一种自定义类型,可以将不同类型的同属于这个类的属性(成员变量)集合在一起,换句话说这些成员变量属于键值对,那么这种也就是常见的json格式,我们来看下如何将结构体转成json ...
- C语言 | 进阶之路第一关
目录 前言 - 文章概述 深入剖析数据在内存中的存储 数据的类型 整型在内存中的存储 浮点型在内存中的存储 指针进阶 字符指针 指针数组 数组指针 数组传参和指针传参 函数指针 函数指针数组 指向函数 ...
- 一道指针压轴笔试题讲解 (进阶指针必看)
今天我来给大家讲解一道指针压轴笔试题,同时也是进阶指针知识必会的一道题.相信大家在认真梳理清楚这道题后,对指针的知识了解定会更上一层楼 ~ 话不多说,上题目 : 请问程序输出结果是什么 ? 相信大家一 ...
- 理解C语言中指针的声明以及复杂声明的语法
昨天刚把<C程序设计语言>中"指针与数组"章节读完,终于把心中的疑惑彻底解开了.现在记录下我对指针声明的理解,顺便说下如何在C语言中创建复杂声明以及读懂复杂声明. 本文 ...
最新文章
- day3 集合、文件操作、函数、局部变量
- 10.11杭州Clouder lab 十分钟搭建共享应用 2:如何通过日志服务实现用户的日志收集与分析...
- Kruskal算法的C语言程序
- 利用html的header下载文件
- FreeSql (三十三)CodeFirst 类型映射
- Java缓存框架使用EhCache结合Spring AOP
- python启动方法_python进程开启的两种方式
- ar android app,RakugakiAR安卓版
- Tensorflow学习—— 预创建的 Estimator
- MySQL里的日期技巧
- 硬件开源为什么如此之难?
- 如何在Vue项目中使用vw实现移动端适配
- php 发送图片,php+curl 发送图片处理代码分享
- TSAP(2) : 时区切换
- 快逸报表4.0 分组处理
- flex + tomcat + myEclipse环境配置与使用(四)
- 4种方法解决js跨域的实现方式
- Vue 开源项目库汇总
- 更精确的新旧中国居民身份证号码验证算法
- form表单同时提交带文本和图片的数据
热门文章
- python爬取大众点评_Python爬虫,获取大众点评上海地区的餐饮信息!
- 化作太行山谷槐花香——谨以此文纪念我的姐妹Jessy
- 网络性能监控工具Smokeping
- 7-3 判断闰年及星期几 (20 分)
- 利用Python输出九九乘法表
- 未来5年,6大风口行业
- C语言-----计算1-1/2+1/3-1/4+....+1/99-1/100+....直到最后一项的绝对值小于10-4为止
- J2EE高级软件工程师面试题集
- LaTeX——命令注释
- ATF快速扫盲(Quick Start)