带你学C语言-指针(4)

news2024/9/27 21:20:08

目录

​编辑

⚾0.前言

🏀1.回调函数

⚽2.qsort

🏉2.1 qsort函数的模拟实现

🎾3.sizeof与strlen对比

🎾4.结束语


⚾0.前言

        言C之言,聊C之识,以C会友,共向远方。各位CSDN的各位你们好啊,这里是持续分享C语言知识的小赵同学,今天要分享的C语言知识是深入了解指针(4),在这一章,小赵将会和大家继续聊指针的相关内容。✊

🏀1.回调函数

那么首先我们要了解的就是什么是回调函数呢?其实回调函数指的就是我们在使用一个函数的时候,我们讲另一个函数以地址的形式传入这个函数中,然后我们在这个函数中,通过函数指针去调用我们传入的函数,而这个我们传入的函数其实也就是我们回调函数。那么废话不多说它的出现究竟可以给我们的代码带来哪些便捷呢?下面我们看下面这样一个代码。

#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;
}
int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	do
	{
		printf("*************************\n");
		printf(" 1:add             2:sub \n");
		printf(" 3:mul             4:div \n");
		printf(" 0:exit                  \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("ret = %d\n", ret);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

这一个代码 其实也就是我们上一章节时候使用的计算器的代码,在上一章其实我们已经用过我们的函数指针数组对这个代码进行过一次优化,那么这个代码能否用我们的回调函数进行优化呢?那么该如何优化呢其实也就是对那他原本的输入数字的方式稍微改一下。

在这里我们先创建一个函数

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             2:sub \n");
		printf(" 3:mul             4:div \n");
		printf(" 0:exit                  \n");
		printf("*************************\n");
		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;
}

⚽2.qsort

在这里请允许小赵为大家介绍一个函数叫qsort函数,这个函数有什么特点,一是实现快速排序,二是它里面使用了我们的回调函数,可以拿来作为我们的训练运用。

可能有人看到这些英文会一阵头疼,但是没关系,如果我们可以看懂它的列子(即知道它的作用)和它的参数就可以完美使用这个函数了。或者我们也可以试着去翻译这个英文,翻译器之类的使用上,实在不行我们就当时恶补英语好了哈哈。好了我们言归正传吗,我们可以看到这个函数它的参数,第一个是我们要排列的数组,第二个是数组的元素数量,第三个则是我们数组中每个元素的大小,最后一个则是比较函数。(这里的排列一定是针对我们元素是一样的,毕竟我们的数组里面的元素就是一样的,不对吗?),这个函数使用上唯一的烦就是我们要自己搞个比较函数,可以说是伤了很多人的脑袋,但实际上也是比较简单的。

(这个是对我们的比较函数的要求即两个数字相减如果为负数则小的在前,大的在后) 

这里举一个整数比较的例子(这里唯一要注意的是这个比较函数的参数类型是const void*)(这里为什么是const void*,因为const void*可以接受任意类型的值)。

#include <stdio.h>
int int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}

当然我们有的时候要比较的不仅仅是数字还有字母等。

他们要怎么比较呢?这里我们就不得不用我们的字符串函数中的strcmp。

 它的整个比较也是和我们上面的输出其实也是一样的。

int int_cmp(const void* p1, const void* p2)
{
	return strcmp((char*)p1, (char*)p2);
}

那么我们在要用哪个的时候实现哪个就行,下面我们来演示两组。

#include <stdio.h>
#include<string.h>
//qosrt函数的使⽤者得实现⼀个⽐较函数
int int_cmp(const void* p1, const void* p2)
{
	return strcmp((char*)p1, (char*)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;
}
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;//结构体指针可以用->来访问结构体内部的东西
}
//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的
//假设按照名字来⽐较
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;
}

🏉2.1 qsort函数的模拟实现

那么这样的一个函数究竟是如何实现的呢?下面我们就来试着去模拟一下这样一个函数的实现。

在排序的底层逻辑上我们可以使用我们的冒泡排序去实现我们的我们的排序,由于我们这一次要排的数据不再是单一的整数型,所以我们的数据交换,还要再弄一个函数,或者在原本函数进行改动(但是在原本函数上改动代码会有点偏长,小赵在这里就再弄一个函数去搞定这件事)。

交换函数

void _swap(void* p1, void* p2, int size)
{
	int i = 0;
	for (i = 0; i < size; i++)//这里其实就是对我们数据的每一个字节进行交换
	{
		char tmp = *((char*)p1 + i);//char刚好占一个一个字节
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = tmp;
	}
}

冒泡排序

void bubble(void* base, int count, int size, int(*cmp)(void*, void*))//这里主要就是冒泡排序前面的知识。
{
	int i = 0;
	int j = 0;
	for (i = 0; i < count - 1; i++)
	{
		for (j = 0; j < count - i - 1; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)//这里是用了我们的比较函数,如果前面比后面的大
			{
				_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

 最后浓缩在一起

#include<stdio.h>
int int_cmp(const void* p1, const void* p2)//比较函数
{
	return (*(int*)p1 - *(int*)p2);
}
void _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 bubble(void* base, int count, int size, int(*cmp)(void*, void*))//排序
{
	int i = 0;
	int j = 0;
	for (i = 0; i < count - 1; i++)
	{
		for (j = 0; j < count - i - 1; 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[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
	int i = 0;
	bubble(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;
}

这样一个函数就可以对我们任意数据进行比较。 

🎾3.sizeof与strlen对比

sizeof和strlen函数其实也是我前面一直在和大家重点聊的两个家伙,这两个家伙我们在一起聊的时间其实很多,给小赵的感悟也很多,那么我们下面就再来看看这两个家伙究竟是什么不同,其实说起来也简单。

首先是sizeof这个操作符,这个函数的作用就是就算你给的东西所占的内存大小。而strlen则是统计我们字符串的长度或者字符串数组的长度,到‘\0’为止。可以说这个家伙真正打起来其实是在数组,如果不是在数组中这两个家伙几乎也打不起来。

好了下面我们结合我们的代码来聊

#include <stdio.h>
int main()
{
 char arr1[3] = {'a', 'b', 'c'};
 char arr2[] = "abc";
 printf("%d\n", strlen(arr1));
 printf("%d\n", strlen(arr2));
 
 printf("%d\n", sizeof(arr1));
 printf("%d\n", sizeof(arr1));
 return 0;
}

(这里要提一下我们的sizeof 可不会管你什么\0不\0只要你存在,那你今天就必须被记录下来。)

我们看到在字符串的统计中两个人都是很正常的,唯有在数组中我们的strlen似乎出了问题,那究竟 是什么问题呢?其实也很简单就是我们之前说的,strlen这个函数比较轴它一定要找到‘\0’为止,那么我们的字符串其实是会自动补一个‘\0’在字符串后面的,那我们的数组呢?我们的数组什么都不会补,那么我们死轴死轴的strlen就会一直去找一直去找我们的‘\0’终于它在一片茫茫的内存的黑暗中找到了我们的'\0',并且返回了它的位置,其实strlen函数这样也有点轴的可爱,不是吗?)那这个问题怎么解决呢?其实就是我们只需要在我们的第一个数组后面补一个‘\0’就可以了

🎾4.结束语

好了小赵今天的分享就到这里了,如果大家有什么不明白的地方可以在小赵的下方留言哦,同时如果小赵有什么地方说得不对也希望得到大家的指点,谢谢各位家人们的支持。你们的支持是小赵创作的动力,加油。

如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持小赵,如有不足还请指点,小赵及时改正,感谢大家支持!!!

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

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

相关文章

selenium处理下拉框

当想要爬取的数据由下拉框来选择时&#xff0c;应该如何处理&#xff1f; 页面如下&#xff1a; 目的获得电影的详细信息&#xff0c;包括票房&#xff0c;上映日期等。 代码如下&#xff1a; from selenium import webdriver from selenium.webdriver.support.select impor…

28个炫酷的CSS特效动画示例(含源代码)

CSS是网页的三驾马车之一&#xff0c;是对页面布局的总管家&#xff0c;2024年了&#xff0c;这里列出28个超级炫酷的CSS动画示例&#xff0c;让您的网站更加炫目多彩。 文章目录 1. 涌动的弹簧效果2. 超逼真的3D篮球弹跳&#xff0c;含挤压弹起模态3. 鼠标放div上&#xff0c;…

linux磁盘,分区,挂载等等

1. 修改磁盘分区的标签 例如&#xff1a;733be18b-7baf-d84c-879d-ca3db465f179太长了&#xff0c;修改一下。 linuxchenxiao:/media/linux/733be18b-7baf-d84c-879d-ca3db465f179$ 先 sudo blkid sudo blkid 找到你想修改的UUID(唯一标识符) /dev/sda1: UUID"733be…

VBA_MF系列技术资料1-315

MF系列VBA技术资料 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-04属于…

【LeetCode热题100】【子串】滑动窗口最大值

题目 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1a; 输入&#xff1a;nums [1,3,-1,-3,5,3,6,7], …

华尔街日报:中国加密货币交易“非法却盛行”,VPN翻墙、微信找币商、线下面交……

《华尔街日报》戏谑地称&#xff0c;中国的投资者曾经是加密货币交易的主导力量&#xff0c;人民币是用于交易比特币最受欢迎的法定货币。而现在&#xff0c;中国的币圈投资者正努力规避政府对加密货币交易的严格规定。 事实上&#xff0c;在过去几年里&#xff0c;中国大陆与加…

基于springboot+vue的旅游网站系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目背景…

各模块的实现

注册模块&#xff1a; 注册使用手机号发送验证码注册的方式&#xff0c;使用的是阿里云的短信发送服务&#xff0c;然后进行认证&#xff0c;有个60s的时间&#xff0c;可以存到redis中&#xff0c;key是手机号&#xff0c;value是验证码。 使用Spring自带的BCryptPasswordEn…

什么是OSPF?为什么需要OSPF?OSPF基础概念

什么是OSPF&#xff1f; 开放式最短路径优先OSPF&#xff08;Open Shortest Path First&#xff09;是IETF组织开发的一个基于链路状态的内部网关协议&#xff08;Interior Gateway Protocol&#xff09;。 目前针对IPv4协议使用的是OSPF Version 2&#xff08;RFC2328&#x…

【图像分类】【深度学习】【轻量级网络】【Pytorch版本】EfficientNet_V1模型算法详解

【图像分类】【深度学习】【轻量级网络】【Pytorch版本】EfficientNet_V1模型算法详解 文章目录 【图像分类】【深度学习】【轻量级网络】【Pytorch版本】EfficientNet_V1模型算法详解前言EfficientNet_V1讲解问题辨析(Problem Formulation)缩放尺寸(Scaling Dimensions)复合缩…

SQL 最大连续合格次数 最大连胜记录次数 最大连败记录次数

有这样一个问题&#xff0c;工厂中要统计某个供应商送货检验的情况&#xff0c;依照其连续合格次数&#xff0c;决定是否免检&#xff0c;不使用游标或者循环&#xff0c;如何写这个sql。 此情景也可以用于统计连胜记录等 先要学习一下 窗函数LAG&#xff0c;指的是按分组和排…

【JavaEE进阶】 依赖注⼊DI详解

文章目录 &#x1f334;什么是依赖注入&#x1f384;依赖注入的三种方法&#x1f6a9;属性注⼊(Field Injection)&#x1f6a9;构造⽅法注⼊&#x1f6a9;Setter注⼊&#x1f6a9;三种注⼊的优缺点 &#x1f333;Autowired存在的问题&#x1f332;解决Autowired存在的问题&…

1.19(232.用栈实现队列)

1.19(232.用栈实现队列) 在push数据的时候&#xff0c;只要数据放进输入栈就好&#xff0c;但在pop的时候&#xff0c;操作就复杂一些&#xff0c;输出栈如果为空&#xff0c;就把进栈数据全部导入进来&#xff08;注意是全部导入&#xff09;&#xff0c;再从出栈弹出数据&a…

JDBC编程详细教程与示例源码

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl JDBC概述 为了在Java语言中提供对数据库访问的支持&#xff0c;Sun公司于1996年提供了一套访问数据库的标准Java类库JDBC。JDBC的全称是Java数据库连接(Java Database Conn…

fabric.js 组件 图片上传裁剪并进行自定义区域标记

目录 0. 前言 1. 安装fabric与引入 2. fabric组件的使用 3. 属性相关设置 4. 初始化加载 4. 方法 5. 全代码 0. 前言 利用fabric组件&#xff0c;实现图片上传、图片”裁剪“、自定义的区域标记一系列操作 先放一张效果图吧&#x1f447; 1. 安装fabric与引入 npm i …

Pytorch从零开始实战17

Pytorch从零开始实战——生成对抗网络入门 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——生成对抗网络入门环境准备模型定义开始训练总结 环境准备 本文基于Jupyter notebook&#xff0c;使用Python3.8&#xff0c;Pytorch1.8cpu&#xf…

【MIdjourney】一些材质相关的关键词

1.多维剪纸(Multidimensional papercut) "Multidimensional papercut"&#xff08;多维剪纸&#xff09;是一种剪纸艺术形式&#xff0c;通过多层次的剪纸技巧和设计来创造出立体感和深度感。这种艺术形式通常涉及在不同的纸层上剪裁不同的图案&#xff0c;并将它们…

Node.js基础知识点(四)

本节介绍一下最简单的http服务 一.http 可以使用Node 非常轻松的构建一个web服务器&#xff0c;在 Node 中专门提供了一个核心模块&#xff1a;http http 这个模块的就可以帮你创建编写服务器。 1. 加载 http 核心模块 var http require(http) 2. 使用 http.createServe…

Java学习(二十一)--JDBC/数据库连接池

为什么需要 传统JDBC数据库连接&#xff0c;使用DriverManager来获取&#xff1b; 每次向数据库建立连接时都要将Connection加载到内存中&#xff0c;再验证IP地址、用户名和密码&#xff08;0.05s~1s)时间。 需要数据库连接时候&#xff0c;就向数据库要求一个&#xf…

卷积神经网络简介-AI快速进阶系列

1. 概述 在本教程中&#xff0c;我们将研究卷积神经网络背后的理论及其架构。 我们将首先讨论通常使用卷积神经网络 &#xff08;CNN&#xff09; 执行的任务和特征提取问题。然后&#xff0c;我们将讨论为什么需要CNN&#xff0c;以及为什么传统的前馈神经网络是不够的。 然…