当前位置: 首页 > news >正文

【c语言——指针详解(4)】


文章目录

  • 一、回调函数是什么?
  • 二、qsort的使⽤
    • 1、使⽤qsort函数排序整型数据
    • 2、使⽤qsort排序结构数据
  • 三、qsort函数的模拟实现


作者主页

一、回调函数是什么?

回调函数就是⼀个通过函数指针调⽤的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数
时,被调⽤的函数就是回调函数。在上一篇文章中,我们实现计算器的第三种代码其实就是运用了回调函数。

//回调函数
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 (*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("请输入2个数\n");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);//Add是地址,传递给函数指针Calc,Calc再去调用函数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;
}

相似的代码抽象成函数:有了函数指针后,函数的调用,可以使用函数名来调用,也可以使用函数指针来调用。

二、qsort的使⽤

#include<stdlib.h>//qsort函数的头文件
#include<string.h>//strcmp的头文件

qsort是用来排序的库函数,直接用来排序数据
底层使用的是快速排序的方式,qsort函数可以排序任意类型的数据
排序方式有很多种,例如:
选择排序
插入排序
冒泡排序
快速排序
希尔排序

1、使⽤qsort函数排序整型数据

使用qsort函数前,我们先写一个冒泡排序的函数,对一组整型数据进行排序,排序为升序。

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[] = { 9,8,7,6,5,4,3,2,1,0 };//排为升序int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);print_arr(arr, sz);return 0;
}

像这个函数只能排序整型数据,如果要排序其他类型数据的时候就无能为力了,这时就要用到qsort函数了。
接下来我们看一下qsort函数的原型:


void qsort(void* base, //指针,指向的是待排序的数组的第一个元素size_t num, //是base指向的待排序数组的元素个数size_t size,//base指向的待排序数组的元素的大小int(*compar)(const void*)//函数指针-指向的就是两个元素的比较函数)void*类型的指针是无具体类型的指针,它的作用就是接收任何类型的地址。
这种类型的指针不能直接解引用,也不能+-整数的运算qsort 函数的使用者-明确的知道要排序的是什么数据,这些数据应该如何比较,所以提供两个元素的比较函数

用qsort函数来实现一下冒泡排序:

#include<stdlib.h>//qsort函数的头文件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* p1, const void* p2)//比较函数
{/* if (*(int*)p1 > *(int*)p2)//将void*类型的指针p1强制转换为int*类型再解引用,p1和p2就是两个要比较的元素,如9和8{return 1;}else if (*(int*)p1 == *(int*)p2)return 0;elsereturn -1; *///简化:return *(int*)p1 - *(int*)p2;//想要降序就交换这两个数据
}void test1()
{int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);//4个参数print_arr(arr, sz);
}int main()
{test1();return 0;
}

在这里插入图片描述

2、使⽤qsort排序结构数据

写一段代码私用qsort排序结构体的数据:

struct Stu
{char name[20];int age;
};

这里的两个结构体元素怎么比较大小?

  1. 按照名字比较-字符串比较–strcmp,strcmp是按照对应着字符串中的字符的ASCII码值比较的
  2. 按照年龄比较-整型比较
#include<stdlib.h>//qsort函数的头文件
#include<string.h>//strcmp的头文件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; // 升序  // return ((struct Stu*)p2)->age - ((struct Stu*)p1)->age; // 降序  
}int cmp_stu_by_name(const void* p1, const void* p2) //按名字排序
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);//强制类型转换为结构体指针
}void print_stu_arr(struct Stu arr[], int sz) //打印结构体数据
{for (int i = 0; i < sz; i++) {printf("Name: %s, Age: %d\n", arr[i].name, arr[i].age);}
}void test2() 
{struct Stu arr[3] = { {"zhangsan", 20}, {"lisi", 35}, {"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);//qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);//按年龄排序qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);//按名字排序print_stu_arr(arr, sz);
}int main() 
{test2();return 0;
}

三、qsort函数的模拟实现

可以使⽤回调函数,来模拟实现qsort(采⽤冒泡的⽅式),bubble—sort来模拟实现qsort,。在这之前我们先看一组代码。


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;}}}
}

上述代码可以用来排序整数数据。
在使用冒泡排序的情况下,假设我要改造这个函数,让他能够排序任意类型的数据,哪些地方需要改造呢?我们知道两个整型元素可以直接使用>比较,但是两个字符串,两个结构体元素是不能使用>比较的。
可以把两个元素比较的方法,封装成函数,然后把函数的地址传给排序函数。

#include<stdlib.h>//qsort函数的头文件
#include<string.h>//strcmp的头文件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* p1, const void* p2) //比较函数
{return (*(int*)p1 - *(int*)p2); // 升序  // return (*(int*)p2 - *(int*)p1); // 降序  
}void Swap(char* buf1, char* buf2, size_t width) //这是对地址的四个字节(width)一 一交换从而实现整个交换
{int i = 0;for ( i = 0; i < width; i++) {char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{//趟数int i = 0;for (i = 0; i < sz - 1; i++){//一趟内部的两两比较int j = 0;for (j = 0; j < sz - 1 - i; j++){//比较arr[j]和arr[j+1]if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)//改变{//交换两个元素Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}void test3() 
{int arr[] = { 3, 1, 7, 8, 5, 3, 4, 9, 0, 6 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
}int main() 
{test3();return 0;
}

在这里插入图片描述

这段代码实现了一个冒泡排序算法,用于对一个整型数组进行排序,并打印排序后的结果。

  • print_arr(int arr[], int sz):这个函数接受一个整型数组arr和数组的大小sz作为参数,并打印数组中的每个元素。
  • cmp_int(const void* p1, const void* p2):这是一个比较函数,用于比较两个整型值。它接受两个指向void的指针(任何类型),将它们转换为指向int的指针,并返回两个整数的差值。这个函数的返回值用于确定排序的顺序(升序或降序)。
  • Swap(char* buf1, char* buf2, size_t width):这个函数用于交换两个元素的内容。它接受两个指向char的指针(代表元素的起始地址)和一个size_t类型的值width(代表元素的宽度,以字节为单位)。然后,它逐个字节地交换两个元素的内容。注意,对于整型数组来说,这种方法不是最高效的,因为它逐个字节地处理数据,而不是直接交换整个整型值。
  • bubble_sort(void* base, size_t sz, size_t width, int (cmp)(const void p1, const void* p2)):这是冒泡排序算法的实现。它接受一个指向数组起始位置的指针base、数组的大小sz、元素的宽度width和一个比较函数cmp作为参数。算法通过多次遍历数组,比较并交换相邻的元素,直到数组完全排序。

同样这个模拟实现的冒泡排序算法也能对结构体元素进行比较:

#include<stdlib.h>//qsort函数的头文件
#include<string.h>//strcmp的头文件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; // 升序  // return ((struct Stu*)p2)->age - ((struct Stu*)p1)->age; // 降序  
}int cmp_stu_by_name(const void* p1, const void* p2) //按名字排序
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);//强制类型转换为结构体指针
}void print_stu_arr(struct Stu arr[], int sz) //打印结构体数据
{for (int i = 0; i < sz; i++){printf("Name: %s, Age: %d\n", arr[i].name, arr[i].age);}
}void Swap(char* buf1, char* buf2, size_t width) //这是对地址的四个字节(width)一 一交换从而实现整个交换
{int i = 0;for ( i = 0; i < width; i++) {char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{//趟数int i = 0;for (i = 0; i < sz - 1; i++){//一趟内部的两两比较int j = 0;for (j = 0; j < sz - 1 - i; j++){//比较arr[j]和arr[j+1]if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)//改变{//交换两个元素Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}void test4()
{struct Stu arr[3] = { {"zhangsan",20},{"lisi",35},{"wangwu",18} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);//bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);print_stu_arr(arr, sz);
}int main() 
{test4();return 0;
}

按名字排序:
在这里插入图片描述

相关文章:

【c语言——指针详解(4)】

文章目录 一、回调函数是什么&#xff1f;二、qsort的使⽤1、使⽤qsort函数排序整型数据2、使⽤qsort排序结构数据 三、qsort函数的模拟实现 作者主页 一、回调函数是什么&#xff1f; 回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数的指针&#xff08;地址&#xf…...

C# (.net6)实现Redis发布和订阅简单案例

概念&#xff1a; 在 .NET 6 中使用 Redis 的/订发布阅模式。发布/订阅&#xff08;Pub/Sub&#xff09;是 Redis 支持的一种消息传递模式&#xff0c;其中一个或多个发布者向一个或多个订阅者发送消息,Redis 客户端可以订阅任意数量的频道。 多个客户端可以订阅一个相同的频道…...

【golang】gorm 使用map实现in 条件查询用法

当 where 字典的值为数组时 gorm 会自动转换为条件 IN 查询 where : map[string]interface{}{} where["id"] [1,2,3] where["name"] "zhangsan"type userList struct {Id int "gorm:id"Name string "gorm:name" } Table.…...

理论篇| 移动端爬虫

移动应用的快速发展和广泛普及带来了海量的数据,这些数据对于市场分析、用户行为洞察和业务优化具有重要价值。然而,由于移动应用的特殊性和防护措施,传统的爬虫技术在采集移动应用数据方面面临许多挑战。因此,App爬虫采集与逆向在爬虫领域的重要性不可低估 然而,App采集…...

systemd实现seatunnel自动化启停

在 systemd 中,您可以通过配置服务单元文件来设置服务在失败或退出后自动重启。这对于确保关键服务在意外退出时能够自动恢复运行非常有用。下面是实现 systemd 自动重启服务的步骤: 通用操作 1. 创建或编辑服务单元文件 假设服务单元文件位于 /etc/systemd/system/my-ser…...

MySQL-08.DDL-表结构操作-创建-案例

一.MySQL创建表的方式 1.首先根据需求文档定义出原型字段&#xff0c;即从需求文档中可以直接设计出来的字段 2.再在原型字段的基础上加上一些基础字段&#xff0c;构成整个表结构的设计 我们采用基于图形化界面的方式来创建表结构 二.案例 原型字段 各字段设计如下&…...

完成Sentinel-Dashboard控制台数据的持久化-同步到Nacos

本次案例采用的是Sentinel1.8.8版本 一、Sentinel源码环境搭建 1、下载Sentinel源码工程 git clone https://github.com/alibaba/Sentinel.git 2、导入到idea 这里可以先运行DashboardApplication.java试一下是否运行成功&#xff0c;若成功&#xff0c;源码环境搭建完毕&a…...

RocketMq详解:三、RocketMq通用生产和消费方法改造

文章目录 1.背景2.通用方法改造2.1添加maven依赖2.2 RocketMq基础配置2.3 配置类2.5 消息传输的对象和结果2.4 消息生产者2.5 消息消费者2.6 功能测试 1.背景 在第二章&#xff1a;《RocketMq详解&#xff1a;二、SpringBoot集成RocketMq》中我们已经实现了消费基本生产和消费…...

基于SpringBoot+Vue+Uniapp的仓库点单小程序的详细设计和实现

2. 详细视频演示 文章底部名片&#xff0c;联系我获取更详细的演示视频 3. 论文参考 4. 项目运行截图 代码运行效果图 代码运行效果图 代码运行效果图 代码运行效果图代码运行效果图 代码运行效果图 5. 技术框架 5.1 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发…...

R语言从多波段tif数据中逐个提取单波段数据

在遥感和地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;将多个波段存储在一个文件中可以更有效地进行数据压缩和管理&#xff0c;减少了存储空间的需求。 在R语言中&#xff0c;处理多波段栅格数据通常涉及以下步骤&#xff1a; 读取数据&#xff1a;使用raster包中…...

华为海思:大小海思的双轮驱动战略分析

华为海思,作为华为旗下的半导体设计部门,近年来在芯片设计领域取得了显著成就,成为了中国乃至全球芯片设计的重要力量。实际上,华为海思并非单一实体,而是由两个主要分支构成:大海思和小海思。这两个分支虽然同属华为海思,但在定位、产品布局以及市场策略上有所不同,共…...

LeetCode | 704.二分查找

标准的二分查找&#xff0c;直接上模板&#xff01; class Solution(object):def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""l 0r len(nums) - 1while l < r:mid (l r 1) / 2if nums[mid] …...

TCP三握四挥

TCP三握(简述) 一开始&#xff0c;客户端和服务端都处于closed状态&#xff0c;服务端主动监听某个端口&#xff0c;处于listen状态 一握要进行C-S的第一个SYN发送&#xff0c;客户端会随机初始化序列号(client_isn)并将其置于TCP首部的序列号字段中&#xff0c;并且将SYN标志…...

java项目之大型商场应急预案管理系统(源码+文档)

项目简介 大型商场应急预案管理系统实现了以下功能&#xff1a; 大型商场应急预案管理系统的主要使用者管理员功能有个人中心&#xff0c;员工管理&#xff0c;预案信息管理&#xff0c;预案类型管理&#xff0c;事件类型管理&#xff0c;预案类型统计管理&#xff0c;事件类…...

【C++】--内存管理

&#x1f47e;个人主页: 起名字真南 &#x1f47b;个人专栏:【数据结构初阶】 【C语言】 【C】 目录 1 C/C内存分布2 C语言中动态内存管理方式 &#xff1a;3 C内存管理方式3.1 new/delete操作内置类型3.2 new和delete操作自定义类型 4 operator new与operator delete4.1 opera…...

【设计模式系列】模板方法模式

一、什么是模板方法模式 模板方法模式&#xff08;Template Method Pattern&#xff09;是一种行为型设计模式&#xff0c;它在父类中定义一个算法的框架&#xff0c;允许子类在不改变算法结构的情况下重写算法的某些特定步骤。这种模式非常适合于那些存在共同行为的类&#x…...

java8 Stream流详细API及用法

目录 整理的更全面的API及用法 创建Stream流 中间操作 filter 过滤 map 映射 flatMap 扁平映射 sorted 排序 limit 截断 skip 跳过 distinct 去重 peek 遍历 终端操作 forEach 遍历 forEachOrdered 顺序遍历 min 统计最小值 max 统计最大值 count 统计元素数量 f…...

Redis——持久化

文章目录 Redis持久化Redis的两种持久化的策略定期备份&#xff1a;RDB触发机制rdb的触发时机&#xff1a;手动执行save&bgsave保存测试不手动执行bgsave测试bgsave操作流程测试通过配置&#xff0c;自动生成rdb快照RDB的优缺点 实时备份&#xff1a;AOFAOF是否会影响到red…...

川字结构布局/国字结构布局

1.串字结构布局 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style&g…...

2013年国赛高教杯数学建模C题古塔的变形解题全过程文档及程序

2013年国赛高教杯数学建模 C题 古塔的变形 由于长时间承受自重、气温、风力等各种作用&#xff0c;偶然还要受地震、飓风的影响&#xff0c;古塔会产生各种变形&#xff0c;诸如倾斜、弯曲、扭曲等。为保护古塔&#xff0c;文物部门需适时对古塔进行观测&#xff0c;了解各种变…...

构建高可用代理池:开源工具agentpull的架构解析与实战部署

1. 项目概述&#xff1a;一个轻量级、可编程的代理拉取工具最近在折腾一些自动化任务和分布式爬虫时&#xff0c;经常遇到一个头疼的问题&#xff1a;如何高效、稳定地管理海量的代理IP资源。无论是数据采集、社交媒体运营还是安全测试&#xff0c;一个可靠的代理池都是基础设施…...

WuKongIM:Go语言轻量级即时通讯内核架构解析与实战部署

1. 项目概述&#xff1a;一个为现代应用而生的即时通讯内核如果你正在开发一个需要实时消息功能的项目&#xff0c;无论是社交App、企业协同工具&#xff0c;还是物联网设备的管理后台&#xff0c;那么“消息收发”这个核心功能大概率会让你头疼。市面上的开源IM方案不少&#…...

在OpenClaw中配置Taotoken作为其AI模型供应商的详细步骤

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 在OpenClaw中配置Taotoken作为其AI模型供应商的详细步骤 OpenClaw是一个功能强大的AI智能体开发框架&#xff0c;它允许开发者灵活…...

Ollama客户端开发指南:构建本地大模型交互工具的核心原理与实践

1. 项目概述&#xff1a;一个与Ollama对话的客户端工具如果你正在本地运行像Llama 3、Mistral或者Qwen这类开源大语言模型&#xff0c;那么Ollama这个名字对你来说一定不陌生。它让部署和管理这些模型变得像在命令行里敲几个单词一样简单。但Ollama本身主要是一个服务端工具&am…...

告别硬盘数据丢失焦虑!电脑专属5种恢复方法,无踩坑,速存

日常使用电脑时&#xff0c;文件误删是高频突发状况——辛苦整理的办公文档、珍藏的生活影像、重要的程序安装包&#xff0c;一旦不小心删除&#xff0c;难免让人手足无措。好在2026年&#xff0c;随着数据存储技术的迭代与恢复工具的升级&#xff0c;电脑误删文件的恢复成功率…...

保姆级教程:在i.MX6ULL开发板上用LVGL v8.3.11跑个炫酷UI(附触屏配置)

嵌入式Linux系统LVGL图形库移植实战指南&#xff1a;从零构建炫酷UI界面 在嵌入式系统开发中&#xff0c;图形用户界面(GUI)的实现一直是开发者面临的挑战之一。传统解决方案要么过于笨重&#xff0c;要么功能简陋&#xff0c;直到LVGL的出现改变了这一局面。这款轻量级开源图形…...

ARMv8浮点运算单元与MVFR寄存器深度解析

1. ARMv8浮点运算单元架构解析在移动计算和嵌入式系统领域&#xff0c;ARMv8架构已经成为事实上的行业标准。作为其核心计算能力的重要组成部分&#xff0c;浮点运算单元(FPU)和高级SIMD(Neon)扩展的性能直接影响着机器学习、图形处理、科学计算等关键应用的执行效率。与x86架构…...

基于龙芯2K1000LA的可信计算在工业边缘安全中的实践

1. 项目概述&#xff1a;当“可信计算”遇上工业边缘 最近在做一个工业数据采集与边缘处理的项目&#xff0c;客户对数据安全的要求提到了前所未有的高度。他们不仅担心数据在传输过程中被窃取&#xff0c;更担心边缘设备本身被恶意篡改&#xff0c;导致采集的数据在源头就“失…...

如何在英雄联盟国服免费体验所有皮肤:R3nzSkin换肤工具终极指南

如何在英雄联盟国服免费体验所有皮肤&#xff1a;R3nzSkin换肤工具终极指南 【免费下载链接】R3nzSkin-For-China-Server Skin changer for League of Legends (LOL) 项目地址: https://gitcode.com/gh_mirrors/r3/R3nzSkin-For-China-Server 想要在英雄联盟国服中免费体…...

基于Claude API的全栈AI应用开发框架:从架构设计到生产部署

1. 项目概述与核心价值最近在折腾AI应用开发&#xff0c;特别是想把手头的一些想法快速落地成可交互的Web应用。相信很多开发者都有类似的痛点&#xff1a;大模型API调用起来简单&#xff0c;但要把想法变成一个功能完整、界面友好、还能稳定部署的应用&#xff0c;中间隔着一道…...