指针的进阶2

六、函数指针数组

字符指针数组 - 存放字符指针的数组 char* arr[10]

整型指针数组 - 存放整型指针的数组 int* arr[10]

函数指针数组 - 存放函数指针的数组

void my_strlen()
{}
int main()
{//指针数组char* ch[5];int arr[10] = {0};//pa是是数组指针int (*pa)[10] = &arr;//pf是函数指针int (*pf)(const void*) = &my_strlen;//函数指针数组int (*pf[10])(const void*) = {&my_strlen};//pf的类型是int (*[10])(const void*)return 0;
}

解释pf 先和 [ ] 结合,说明 pf是数组,数组的内容是 int (*)() 类型的函数指针。

6.1转移表

函数指针数组的用途:转移表

举例:写一个计算器实现整数的加、减、乘、除

计算器的一般实现:

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 Mui(int x,int y)
{return x * y;
}int Div(int x,int y)
{return x / y;
}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("请输入两个操作数:>");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;default:printf("选择错误\n");break;}}while(input);return 0;
}

使用函数指针数组实现:

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 Mui(int x,int y)
{return x * y;
}int Div(int x,int y)
{return x / y;
}//用函数指针数组存放上述函数的地址
//转移表
int (*pf[5])(int,int) = {NULL,Add,Sub,Mul,Div};//第一个元素是NULL(0),是为了使得函数与菜单中的选项数字一致int main()
{int input = 0;int x = 0;int y =0;int ret = 0;do{menu();printf("请选择:>");scanf("%d",&input);if(input == 0){printf("退出计算器\n");break;}else if(input >= 1 && innput <=4){printf("请输入两个操作数:>");scanf("%d %d",&x,&y);ret = pf[input](x,y);printf("%d\n",ret);}else{printf("选择错误\n");}}while(input);    return 0;
}

七、指向函数指针数组的指针

指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针

int main()
{//数组指针int arr[10];int (*pa)[10] = &arr;//函数指针int (*pf)(int,int) = &test;//函数指针数组int (*pf[5])(int ,int) = {test};pf[0] = test;//ppf是指向函数指针数组的指针int (*(*ppf)[5])(int,int) = &pf//ppf的类型是int (*(*)[5])(int,int)return 0;
}

八、回调函数

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

可以使用回调函数来实现计算器,使其更加简化:

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 Mui(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;int ret = 0;printf("请输入两个操作数:>");scanf("%d %d",&x,&y);ret = pf(x,y);printf("%d\n",ret);
}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;
}
把调用的函数的地址以参数的形式传递过去,使⽤函数指针接收,函数指针指向什么函数就调⽤什么函数,这⾥其实使⽤的就是回调函数的功能

8.1qsort函数的使用

首先回顾一下冒泡排序:思想:两两相邻的元素进行比较

例如进行升序排序:

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;}}}
}
int main()
{//对数组进行排序,升序int arr[10] = {9,8,7,6,5,4,3,2,1,0};int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr,sz);//0 1 2 3 4 5 6 7 8 9//打印int i = 0;for(i = 0;i <sz; i++){printf("%d ",arr[i]);}return 0;
}

冒泡排序是有缺点的:只能排序整数,结构体、浮点型等不可以

qsort函数的使用

库函数中有一个排序函数:qsort(快速排序)

void* base - 待排序的数组的起始位置(地址) 
size_t num - 数组元素个数
size_t size - 数组中每一个元素的大小(一个元素是几个字节)
int (*compar)(const void*, const void*) - 两个元素的比较函数(函数指针),第一个参数是进行比较的两个元素中第一个的地址,第二个参数是另一个元素地址
比较函数的参数是void*类型的原因:因为无法确定想要比较的类型是int、结构体等类型中的哪一种,所以设置成void(无具体类型)类型

int main()
{int a = 10;float f = 5.5f;int* p = &a;//p = &f;//如果把f的地址取出来赋给p,浮点型的地址赋给整型的指针,编译器会报警告:类型不兼容//如果写成void*的指针类型void* pp = &f;//void*的指针可以接收float类型的地址,且不会报警告pp = &a;//用void*的指针接收整型地址也不会警告//pp - 垃圾桶(可以接受任何类型的地址,因为pp是void*类型的指针变量,无具体类型)printf("%f\n", *pp);//不能直接打印,会报错,void*的指针不能直接解引用pp++;//进行++也不行,因为不知道具体类型,不知道具体加一步要走多远(大小不知道)return 0;
}

但是也有缺点:没有办法直接使用,因为void*的指针不能直接解引用,不知道具体类型,如果想要使用,要进行强制类型转换,就可以使用了

---------------------------------------------------------------------------------------------------------------------------------

进一步解析比较函数:当比较函数接收的两个元素进行比较时,p1>p2 - 返回大于0的数字

p1<p2 - 返回小于0的数字;p1=p2 - 等于0

前面我们说过要对数组进行升序排序,那么我们就可以根据以上的信息,转化为两个元素相减的结果,再根据此结果来判断是否需要进行交换 ,如果相减的结果大于0,说明p1>p2,要进行交换

qsort函数的使用需要包含头文件 #include <stdlib.h>

8.1.1使用qsort函数排序整型数据

#include <stdio.h>
#include <stdlib.h>int cmp_int(const void* p1,const void* p2)
{return *(int*)p1 - *(int*)p2;//升序return *(int*)p2 - *(int*)p1;//降序
}
int main()
{//对数组进行排序,升序int arr[10] = {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);int i = 0;for(i = 0;i <sz; i++){printf("%d ",arr[i]);}return 0;
}

8.1.2使用qsort函数排序结构体数据

按照年龄排序:

#include <stdio.h>
#include <stdlib.h>struct Stu
{char name[20];int age;
};//按照学生的年龄来排序
int cmp_stu_by_age(const void* p1,const void* p2)
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}int main()
{struct Stu s[3] = {{"zhangsan",20},{"lisi",50},{"wangwu",33}};int sz = sizeof(s) / sizeof(s[0]);qsort(s,sz,sizeof(s[0]),cmp_stu_by_age);int i = 0;for(i = 0;i < sz; i++){printf("姓名:%s 年龄:%d\n",s[i].name,s[i].age);}return 0;
}

按照姓名排序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>struct Stu
{char name[20];int age;
};//按照学生的姓名来排序
int cmp_stu_by_name(const void* p1,const void* p2)
{return strcmp(((struct Stu*)p1)->name,((struct Stu*)p2)->name);
}int main()
{struct Stu s[3] = {{"zhangsan",20},{"lisi",50},{"wangwu",33}};int sz = sizeof(s) / sizeof(s[0]);qsort(s,sz,sizeof(s[0]),cmp_stu_by_name);int i = 0;for(i = 0;i < sz; i++){printf("姓名:%s 年龄:%d\n",s[i].name,s[i].age);}return 0;
}

字符串是按照字典序排序的:
字典序是一种基于字母或字符顺序的排列方式,类似于字典中单词的排列规则。在C语言中,常用于字符串或字符数组的比较和排序。

字典序的核心规则‌
逐字符比较‌从左到右逐个比较字符的ASCII。若字符不同,ASCII值较小的字符所在的字符串更小。若字符相同,继续比较下一个字符。
长度优先‌:若所有字符相同,则较短的字符串更小。
‌示例‌:"apple" < "banana"(因为 'a' < 'b')
"app" < "apple"(前三个字符相同,但前者更短)
‌C语言中的字典序实现‌:
使用标准库函数 strcmp
strcmp 是C语言标准库(<string.h>)中用于比较字符串的函数,按字典序返回结果:

‌返回值‌:
< 0:第一个字符串小于第二个。
= 0:两个字符串相等。
> 0:第一个字符串大于第二个。

注意事项:字符编码默认基于ASCII值,大写字母(A-Z,ASCII 65~90)比小写字母(a-z,ASCII 97~122)小

字符串以终止符\0结尾,比较时会自动处理

8.2qsort函数的模拟实现

根据qsort函数,改造冒泡排序,使得这个函数可以排序任意指定的数组(以冒泡排序的思维实现qsort函数)

8.2.1排序整型数组

//使用我们自己写的bubble_sort函数排序整型数组void Swap(char* a,char* b,int size)
{int i = 0;for(i = 0;i < size; i++){char tmp = *a;*a = *b;*b = tmp;a++;b++;}
}
int cmp_int(const void* p1,const void* p2)
{return *(int*)p1 - *(int*)p2;
}
void bubble_sort(void* base,size_t num,size_t size,int (*cmp)(const void* p1,const void* p2))
{//趟数size_t i = 0;for(i = 0;i < num-1; i++){size_t j = 0;for(j = 0;j < num-1-i; j++){if(cmp((char*)base+j*size,(char*)base+(j+1)*size) > 0){Swap((char*)base+j*size,(char*)base+(j+1)*size,size);//实现两个元素的比较}}}
}
int main()
{int arr[10] = {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);int i = 0;for(i = 0;i < sz; i++){printf("%d ",arr[i]);}return 0;
}

8.2.2排序结构体数组

按照年龄排序:

struct Stu
{char name[20];int age;
};
void Swap(char* a,char* b,int size)
{int i = 0;for (i = 0; i < size; i++){char tmp = *a;*a = *b;*b = tmp;a++;b++;}
}
//按照年龄排序
int cmp_stu_by_age(const void* p1,const void* p2)
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}
void bubble_sort(void* base,size_t num,size_t size,int (*cmp)(const void* p1,const void* p2))
{//趟数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*size,(char*)base+(j+1)*size) > 0){Swap(cmp((char*)base+j*size,(char*)base+(j+1)*size,size);}}}
}
int main()
{struct Stu s[3] = {{"zhangsan",20},{"lisi",50},{"wangwu",33}};int sz = sizeof(s) / sizeof(s[0]);bubble_sort(s,sz,sizeof(s[0]),cmp_stu_by_age);int i = 0;for(i = 0;i < sz; i++){printf("姓名:%s 年龄:%d\n",s[i].name,s[i].age);}return 0;
}

按照姓名排序:

struct Stu
{char name[20];int age;
};
void Swap(char* a,char* b,int size)
{int i = 0;for (i = 0; i < size; i++){char tmp = *a;*a = *b;*b = tmp;a++;b++;}
}
//按照姓名排序
int cmp_stu_by_name(const void* p1,const void* p2)
{return strcmp(((struct Stu*)p1)->name,((struct Stu*)p2)->name);
}
void bubble_sort(void* base,size_t num,size_t size,int (*cmp)(const void* p1,const void* p2))
{//趟数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*size,(char*)base+(j+1)*size) > 0){Swap(cmp((char*)base+j*size,(char*)base+(j+1)*size,size);}}}
}
int main()
{struct Stu s[3] = {{"zhangsan",20},{"lisi",50},{"wangwu",33}};int sz = sizeof(s) / sizeof(s[0]);bubble_sort(s,sz,sizeof(s[0]),cmp_stu_by_name);int i = 0;for(i = 0;i < sz; i++){printf("姓名:%s 年龄:%d\n",s[i].name,s[i].age);}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/75527.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

速盾:高防CDN节点对收录有影响吗?

引言 搜索引擎收录是网站运营中至关重要的环节&#xff0c;它直接影响着网站的曝光度和流量。近年来&#xff0c;随着网络安全威胁的增加&#xff0c;许多企业开始采用高防CDN&#xff08;内容分发网络&#xff09;来保护其网站免受DDoS攻击和其他形式的网络攻击。然而&#x…

2025蓝桥杯省赛C/C++研究生组游记

前言 至少半年没写算法题了&#xff0c;手生了不少&#xff0c;由于python写太多导致行末老是忘记打分号&#xff0c;printf老是忘记写f&#xff0c;for和if的括号也老是忘写&#xff0c;差点连&&和||都忘记了。 题目都是回忆版本&#xff0c;可能有不准确的地方。 …

Quill富文本编辑器支持自定义字体(包括新旧两个版本,支持Windings 2字体)

文章目录 1 新版&#xff08;Quill2 以上版本&#xff09;2 旧版&#xff08;Quill1版本&#xff09; 1 新版&#xff08;Quill2 以上版本&#xff09; 注意&#xff1a;新版设置 style"font-family: Wingdings 2" 这种带空格的字体样式会被过滤掉&#xff0c;故需特…

dbt:新一代数据转换工具

dbt&#xff08;Data Build Tool&#xff09;一款专为数据分析和工程师设计的开源工具&#xff0c;专注于 ETL/ELT 流程的数据转换&#xff08;Transform&#xff09;环节&#xff0c;帮助用户以高效、可维护的方式将原始数据转换为适合分析的数据模型。 用户只需要编写查询&am…

【家政平台开发(39)】解锁家政平台测试秘籍:计划与策略全解析

本【家政平台开发】专栏聚焦家政平台从 0 到 1 的全流程打造。从前期需求分析,剖析家政行业现状、挖掘用户需求与梳理功能要点,到系统设计阶段的架构选型、数据库构建,再到开发阶段各模块逐一实现。涵盖移动与 PC 端设计、接口开发及性能优化,测试阶段多维度保障平台质量,…

Java中的Map vs Python字典:核心对比与使用指南

一、核心概念 1. 基本定义 Python字典&#xff08;dict&#xff09; &#xff1a;动态类型键值对集合&#xff0c;语法简洁&#xff0c;支持快速查找。Java Map&#xff1a;接口&#xff0c;常用实现类如 HashMap、LinkedHashMap&#xff0c;需声明键值类型&#xff08;泛型&…

C语言基础之数组

1. 一维数组的创建和初始化 数组的创建 数组是一组相同类型元素的集合。 数组的创建方式&#xff1a; type_t arr_name [const_n]; //type_t 是指数组的元素类型 //const_n是一个常量表达式&#xff0c;用来指定数组的大小 数组创建的实例&#xff1a; //代码1int arr1[10]; …

虚幻引擎5-Unreal Engine笔记之“将MyStudent变量设置为一个BP_Student的实例”这句话如何理解?

虚幻引擎5-Unreal Engine笔记之“将MyStudent变量设置为一个BP_Student的实例”这句话如何理解&#xff1f; code review! 文章目录 虚幻引擎5-Unreal Engine笔记之“将MyStudent变量设置为一个BP_Student的实例”这句话如何理解&#xff1f;理解这句话的关键点1.类&#xff08…

提示词 (Prompt)

引言 在生成式 AI 应用中&#xff0c;Prompt&#xff08;提示&#xff09;是与大型语言模型&#xff08;LLM&#xff09;交互的核心输入格式。Prompt 的设计不仅决定了模型理解任务的准确度&#xff0c;还直接影响生成结果的风格、长度、结构与可控性。随着模型能力和应用场景…

十二、C++速通秘籍—静态库,动态库

上一章节&#xff1a; 十一、C速通秘籍—多线程-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/147055932?spm1001.2014.3001.5502 本章节代码&#xff1a; cpp2/library CuiQingCheng/cppstudy - 码云 - 开源中国https://gitee.com/cuiqingcheng/cppst…

什么是继承?js中有哪儿些继承?

1、什么是继承&#xff1f; 继承是面向对象软件技术中的一个概念。 2、js中有哪儿些继承&#xff1f; js中的继承有ES6的类class的继承、原型链继承、构造函数继承、组合继承、寄生组合继承。 2.1 ES6中类的继承 class Parent {constructor() {this.age 18;} }class Chil…

Linux进程通信入门:匿名管道的原理、实现与应用场景

Linux系列 文章目录 Linux系列前言一、进程通信的目的二、进程通信的原理2.1 进程通信是什么2.2 匿名管道通讯的原理 三、进程通讯的使用总结 前言 Linux进程间同通讯&#xff08;IPC&#xff09;是多个进程之间交换数据和协调行为的重要机制&#xff0c;是我们学习Linux操作系…

探秘Transformer系列之(26)--- KV Cache优化 之 PD分离or合并

探秘Transformer系列之&#xff08;26&#xff09;— KV Cache优化 之 PD分离or合并 文章目录 探秘Transformer系列之&#xff08;26&#xff09;--- KV Cache优化 之 PD分离or合并0x00 概述0x01 背景知识1.1 自回归&迭代1.2 KV Cache 0x02 静态批处理2.1 调度策略2.2 问题…

十大PDF解析工具在不同文档类别中的比较研究

PDF解析对于包括文档分类、信息提取和检索在内的多种自然语言处理任务至关重要&#xff0c;尤其是RAG的背景下。尽管存在各种PDF解析工具&#xff0c;但它们在不同文档类型中的有效性仍缺乏充分研究&#xff0c;尤其是超出学术文档范畴。通过使用DocLayNet数据集&#xff0c;比…

HarmonyOS-ArkUI 装饰器V2 @ObservedV2与@Trace装饰器

参考文档: 文档中心https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V14/arkts-new-observedv2-and-trace-V14#trace%E8%A3%85%E9%A5%B0%E5%AF%B9%E8%B1%A1%E6%95%B0%E7%BB%84由于V2的装饰器比V1的装饰器更加易用,尽管学习的过程中用到的都是V1的装饰器,但…

GPT - GPT(Generative Pre-trained Transformer)模型框架

本节代码主要为实现了一个简化版的 GPT&#xff08;Generative Pre-trained Transformer&#xff09;模型。GPT 是一种基于 Transformer 架构的语言生成模型&#xff0c;主要用于生成自然语言文本。 1. 模型结构 初始化部分 class GPT(nn.Module):def __init__(self, vocab…

基于FPGA的六层电梯智能控制系统 矩阵键盘-数码管 上板仿真均验证通过

基于FPGA的六层电梯智能控制系统 前言一、整体方案二、软件设计总结 前言 本设计基于FPGA实现了一个完整的六层电梯智能控制系统&#xff0c;旨在解决传统电梯控制系统在别墅环境中存在的个性化控制不足、响应速度慢等问题。系统采用Verilog HDL语言编程&#xff0c;基于Cyclo…

车载通信系统中基于ISO26262的功能安全与抗辐照协同设计研究

摘要&#xff1a;随着智能网联汽车的快速发展&#xff0c;车载通信系统正面临着功能安全与抗辐照设计的双重挑战。在高可靠性要求的车载应用场景下&#xff0c;如何实现功能安全标准与抗辐照技术的协同优化&#xff0c;构建满足ISO26262安全完整性等级要求的可靠通信架构&#…

Node.js种cluster模块详解

Node.js 中 cluster 模块全部 API 详解 1. 模块属性 const cluster require(cluster);// 1. isMaster // 判断当前进程是否为主进程 console.log(是否为主进程:, cluster.isMaster);// 2. isWorker // 判断当前进程是否为工作进程 console.log(是否为工作进程:, cluster.isW…

融合动态权重与抗刷机制的网文评分系统——基于优书网、IMDB与Reddit的混合算法实践

✨ Yumuing 博客 &#x1f680; 探索技术的每一个角落&#xff0c;解码世界的每一种可能&#xff01; &#x1f48c; 如果你对 AI 充满好奇&#xff0c;欢迎关注博主&#xff0c;订阅专栏&#xff0c;让我们一起开启这段奇妙的旅程&#xff01; 以权威用户为核心&#xff0c;时…