【C语言】回调函数 和 部分库函数的用法以及模拟实现

news2024/11/23 19:51:29

一、回调函数:

1、定义:

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

2、qsort的模拟实现(回调函数的应用)

那么回调函数有什么实际性的作用呢?下面模拟库函数qsort来顺便了解回调函数的用法:(在这里使用冒泡排序来模拟)
首先要了解什么是qsort:
上面的意思是:对数组的元素进行排序,使用函数确定顺序所排序的类型。
依然从代码入手:
int my_cmp(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

void Swap(char* buf1, char* buf2, size_t width)
{
	for (size_t i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

Bubble_Sort(void* base,size_t sz,size_t width,int (*cmp)(const void* e1,const void* e2))
{
	for (size_t i = 0; i < sz - 1; i++)
	{
		for (size_t j = 0; j < sz - i - 1; j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

void test1()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	Bobble_Sort(arr, sz, sizeof(arr[0]), my_cmp);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	test1();
	return 0;
}

如上所示:我们要想将arr数组排为升序,在模拟实现时,要给四个参数:

1、这个数组名

2、这个数组的元素个数

3、这个数组每个元素的大小(单位字节)

4、指向比较两个元素的函数的指针。(这个来控制是比较整型或者浮点型或者结构体等等)

接收的时候用void*接受是因为不知道我传过来的是什么类型的,在之后强制类型转即可。

上述代码中:
my_cmp为我要排序整型所需要的函数。
Swap为交换两个元素
Bubble_Sort为冒泡排序模拟库函数qsort
在其中:
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
    Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
这行代码是最核心的,
width为偏移量,这样可以通过 回调函数来调用my_cmp来知道我是什么类型的和大小。
将传过来的数组名用void* base接收,后来转换为(char*)这样可以+偏移量来找到每个元素,这也是要传数组每个元素的大小的意义。
在Swap函数中与传统的交换不同的是需要每一个元素的大小,这样可以将一个元素以字节为单位一个一个交换,毕竟如果直接交换的话,就会将代码写死(比如写了个int类型,就不能交换浮点数类型)

在上述代码中就在函数中调用了函数,所以就用了回调函数cmp

二、库函数的用法及部分模拟实现:

首先来了解一下有那些处理字符和字符串的库函数:

1、strlen:

这是用来计算字符串长度的。

//计数
int my_strlen1(const char* src)
{
	assert(&src);

	int count = 0;
	while (*src++)
		count++;
	return count;
}
//递归
int my_strlen2(const char* src)
{
	assert(src);
	
	if (*src != '\0')
		return 1 + my_strlen2(src + 1);
	else
		return  0;
}
//指针-指针
int my_strlen3(const char* src)
{
	assert(src);

	const char* dest = src;
	while (*dest)
	{
		dest++;
	}
	return dest - src;
}

int main()
{
	char arr[] = "abcdefg";
	int ret = my_strlen3(arr);
	printf("%d\n", ret);
	return 0;
}

如上用了三种方法分别模拟了字符串长度的计算。

2、strcpy:

这是将一个字符串拷贝到另外一个字符串中的

char* my_strcpy(char* dest, char* src)
{
	char* ret = dest;
	assert(dest && src);
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[] = "abcdefg";
	char arr2[20] = "";
	my_strcpy(arr2, arr1);
	printf("%s\n", arr1);
	printf("%s\n", arr2);
	return 0;
}

在模拟实现中将src一个个赋给dest,最后返回目标数组的地址

3、strcat:

这是将一个字符串追加在另外一个字符串后面。

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//1.找到要追加的地方
	while (*dest)
	{
		dest++;
	}
	//2.追加
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "ppr";
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

首先找到我要追加的地方,然后进行赋值即可,最后返回被追加的数组首元素。

4、strcmp:

这是比较两个字符串的大小。

如上,如果第一个字符串小于后一个,那么返回一个小于0 的数;

           如果第一个字符串等于后一个,那么返回0 ;

           如果第一个字符串大于后一个,那么返回一个大于0 的数。

(在VS编译器中分别返回1,0,-1)。

int my_strcmp( char* str1, char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	if (*str1 > *str2)
		return 1;
	else
		return -1;
	//return *str1 - *str2;
}

int main()
{
	char arr1[20] = "abqf";
	char arr2[20] = "abqf";
	int ret = my_strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}

5、strncpy:

这些长度受限制的函数引入是因为strcpy函数不安全,

比如在strcpy函数中,如果拷贝过去字符串,此时接收这个字符串的数组不够这么大,就会失败,同样,如果c语言中最开始不声明“#define _CRT_SECURE_NO_WARNINGS 1”就会不可以使用,所以就衍生出了strncpy,这个作用和strcpy几乎是一模一样的,就是多了1个无符号整型参数,作用是我要拷贝过来几个字符。

但是strncpy只是相对于strcpy安全罢了,如果你输入的数字等于这个数组的最大元素,那么就会使‘\0’拷贝不过来,也会出现错误:

所以strncpy只是相对于strcpy安全。

6、strncat:

这个多出来的无符号整型的参数就是我需要追加几个字符数。

7、strncmp:

这个多出来的无符号整型的参数就是我需要比较几个字符数。

8、strstr:

这个函数的作用是在一个字符串中找另外一个字符串。

char* my_strstr(const char* src1, const char* src2)
{
	assert(src1 && src2);
	if (*src2 == '\0')
		return (char*)src1;

	const char* s1 = src1;
	const char* s2 = src2;
	const char* p= src1;

	while (*p)
	{
		s1 = p;
		s2 = src2;

		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return (char*)p;
		p++;
	}
	return NULL;
}

int main()
{
	char arr1[] = "abbbbcdef";
	char arr2[] = "bbc";
	char* arr3 = my_strstr(arr1, arr2);
	if (arr3 == NULL)
	{
		printf("找不到\n");
	}
	else 
	{
		printf("%s\n", arr3);
	}
	return 0;
}

思路:

在src1中找src2,第一个while循环将s1,s2分别指向src1和src2的第一位(这样可以使得每次找完可能相等的位置后,如果不相等就会返回此时记录的位置),之后来找可能相等的位置,在第二个while循环中来看看是否相等。

9、strtok:

这是属于一个字符串的分割,

第一个参数为我所需要分割的起始位置,

在后续的调用中在第一个参数位置只需传空指针即可。

第二个参数中传我所分割的标志集合。

10、strerror:

这是一个打印错误信息的函数,C语言的库函数在运行的时候,如果发生错误,就会将错误码存在一个变量中,这个变量是:errno(在errno.h这个头文件里面)

错误码是一些数字:1 2 3 4 5

我们需要将错误码翻译成错误信息

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		//perror("fopen");
		return 1;
	}
	//读文件
	//关闭文件
	fclose(pf);
	pf = NULL;
	
	return 0;
}

在上面代码中,在当前路径下读一个叫做test.txt的文件,如果没有就会将错误代码打印出来

拓展:有另外一个函数perror这个用起来更加顺手些,将可以函数错误信息直接打印出来。

可以理解为:printf + strerror

11、memcpy:

这个函数也是拷贝,但是不只局限于字符串的拷贝,属于内存的拷贝,

第一个参数是目标拷贝函数,第二个参数是待拷贝的函数,第三个参数是拷贝多少个字节

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	memcpy(arr2, arr1, 24);
	return 0;
}

模拟实现:

void* my_memcpy(void* dest, void* src, size_t num)
{ 
	void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)src;
		++(char*)dest;
        ++(char*)src;
	}
	return ret;
}


int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr1, 24);
	return 0;
}

我们这个自己模拟的局限性:不能够自我拷贝(但是库函数里的memcpy可以),那么接下来进行优化---------引入了memmove函数的模拟实现

12、memmove:

解决方法:如下图,若src在dest后面,就将src从前往后拷贝,若src在dest前面就从后往前拷贝。

void* my_memmove(void* dest, void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	if (dest < src)
	{
		//前->后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
        //后->前
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}

}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1+2, arr1, 20);
	return 0;
}

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

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

相关文章

高效、智能、安全:小型机房EasyCVR+AI视频综合监控解决方案

一、背景需求分析 随着信息技术的迅猛发展&#xff0c;小型机房在企事业单位中扮演着越来越重要的角色。为了确保机房的安全稳定运行&#xff0c;远程监控成为了必不可少的手段。 二、视频监控 视频监控是机房远程监控的重要组成部分。通过安装IP摄像机及部署视频监控系统Ea…

【Linux】基础IO——理解文件系统

1.理解文件系统 1.1.ls与stat 磁盘文件由两部分构成&#xff0c;分别是文件内容和文件属性。 文件内容就是文件当中存储的数据&#xff0c;文件属性就是文件的一些基本信息&#xff0c; 例如文件名、文件大小以及文件创建时间等信息都是文件属性&#xff0c;文件属性又被称…

android常用知识

透明activity样式&#xff1a; android:theme"android:style/Theme.Translucent.NoTitleBar.Fullscreen"这句代码&#xff0c;当你是建的empty activity project时&#xff0c;默认继承的是AppCompat这个类。所以在AndroidMifext.xml文件中用上述代码会导致程序错误&…

反馈型振荡器

目录 反馈型振荡器分类 基本工作原理 启动过程 “心脏”LC振荡 起振条件 平衡条件 稳定条件 互感耦合振荡器 电感三端LC振荡器 电容三端LC振荡器 串联改进电容三端式振荡器 并联改进电容三端式振荡器 相位平衡条件的判断准则 反馈型振荡器分类 基本工作原理 启动过…

心跳机制讲解及实例

什么是心跳机制 心跳机制出现在tcp长连接中&#xff0c;客户端和服务器之见定时发送一种特殊的数据包通知对方还在线&#xff0c;以确保tcp链接地可靠性&#xff0c;有可能tcp链接由于某些原因(列入网线被拔了&#xff0c;突然断电)导致客户端断了&#xff0c;但是服务器不知道…

使用高斯混合模型(GMM)进行猫狗音频聚类(Kaggle Audio Cats and Dogs)

Audio Cats and Dogs | Kaggle 目录 一、实验目标 二、数据分析 三、实验结果 四、改进方向 一、实验目标 数据集包括164个标注为猫的.wav文件&#xff0c;总共1323秒和113个标注为狗叫声的.wav文件&#xff0c;总共598秒&#xff0c;要求判别每个音频是狗叫还是猫叫 二、…

Springboot + Mybatis 实现sql打印

参照这个视频&#xff1a;https://www.bilibili.com/video/BV1MS411N7mn/?vd_source90ebeef3261cec486646b6583e9f45f5 实现mybatis对外暴露的接口Interceptor 使用Intercepts接口,这里的写法参照mybatis-plus中的拦截器写法 Intercepts({Signature(type Executor.class, m…

FPGA开发Vivado安装教程

前言 非常遗憾的一件事情是&#xff0c;在选修课程时我避开了FPGA&#xff0c;选择了其他方向的课程。然而&#xff0c;令我没有想到的是&#xff0c;通信项目设计的题目竟然使用FPGA&#xff0c;这简直是背刺。在仅有的半个月时间里&#xff0c;准备这个项目确实是非常紧张的…

Corrupt JPEG data: 2 extraneous bytes before marker 0xd9

场景 异常&#xff1a;Corrupt JPEG data: 2 extraneous bytes before marker 0xd9 python语言&#xff0c;CV2读图像数据集&#xff0c;训练目标检测模型。在数据集分批送入模型训练过程中&#xff0c;出现大片图片异常情况。 &#xff08;建议直接去看修复图像方法二&…

华翰传媒集团横店影视基地盛大开业,汇剧视界APP震撼发布

2024年6月1日上午&#xff0c;横店影视华翰传媒集团携手腾烨影视隆、明艺影视重举办了横店影视基地的开业庆典。这一盛事不仅标志着华翰传媒集团在影视行业发展的重要里程碑&#xff0c;更彰显了其深耕影视产业、致力于打造高质量影视内容的决心与目标。 活动盛况空前&#xff…

vivado PIP or SITE_PIP、PKGPIN_BYTEGROUP

PIP是Xilinx部件上用于路由连接或网络的设备对象。PIP 称为ARC的连接多路复用器可以编程为将一根电线连接到 另一个&#xff0c;从而将节点连接在一起&#xff0c;以形成中特定NET所需的路由 设计。 SITE_PIP&#xff0c;也称为路由BEL&#xff0c;是SITE内部的连接多路复用器&…

vcs覆盖率相关

查看覆盖率是由哪几个tc覆盖的 选择要查看的覆盖率点&#xff0c;右键选择 show xxx tests&#xff1b; 覆盖率的合并

最新开源:英伟达Nemotron-4 340B,哔哩哔哩Index-1.9B,谷歌RecurrentGemma-9B...

文章目录 1. 英伟达开源Nemotron-4 340B2. 哔哩哔哩开源轻量级模型 Index-1.9B3. 微软开源混合模型 Samba4. 谷歌开源 RecurrentGemma-9B&#xff0c;性能与Gemma相同5. Stable Diffusion 3 Medium&#xff1a;“最强文生图开源 AI 模型” 1. 英伟达开源Nemotron-4 340B 当地时…

【AI绘画】新手小白看这篇就够啦!国产PS AI插件超好入门!

随着人工智能技术的飞速发展&#xff0c;Photoshop作为设计师们不可或缺的工具&#xff0c;也在不断地融入AI技术&#xff0c;以提升设计效率和效果。最近米兔用了一款AI绘画软件StartAI&#xff0c;被其强大的功能和易用性经验到了&#xff0c;下面跟大家详细分享一下这款ps插…

010-基于Sklearn的机器学习入门:聚类(上)

本节及后续章节将介绍深度学习中的几种聚类算法&#xff0c;所选方法都在Sklearn库中聚类模块有具体实现。本节为上篇&#xff0c;将介绍几种相对基础的聚类算法&#xff0c;包括K-均值算法和均值漂移算法。 目录 10.1 聚类概述 10.1.1 聚类的种类 10.1.2 Sklearn聚类子模…

Pycharm的基础使用

Pycharm的基础使用 一、修改主题 第一步&#xff1a;点击file->settings 第二步&#xff1a;找到Appearance&Behavior->Appearance->Theme选择主题 有五种主题可以选 二、修改默认字体和大小 第一步&#xff1a;打开设置与上面修改主题第一步一样&#xff1b…

硕思logo设计师下载-2024官方最新版-logo制作软件安装包下载

硕思​​Logo设计​​师是一款操作灵活简单、功能强大的logo制作​​软件​​。可以通过简单的点击就可以为网站、博客、论坛和邮件创建专业的logo、条幅、按钮、标题、图标和签名等。 硕思logo设计师提供了很多精心设计的模板和丰富的资源&#xff0c;为更好的创建logo艺术作品…

GPT3.5的PPO目标函数怎么来的:From PPO to PPO-ptx

给定当前优化的大模型 π \pi π&#xff0c;以及SFT模型 π S F T \pi_{SFT} πSFT​ 原始优化目标为: max ⁡ E ( s , a ) ∼ R L [ π ( s , a ) π S F T ( s , a ) A π S F T ( s , a ) ] \max E_{(s,a)\sim RL}[\frac{\pi(s,a)}{\pi_{SFT}(s,a)}A^{\pi_{SFT}}(s,a)] m…

光纤通信基础(光纤的构造、工作原理、色散、工作频段、损耗、分类、不同标准及应用、接口类型、常见标示方法、熔接)

文章目录 光纤的构造&#xff1a;纤芯、包层、涂覆层光纤的工作原理&#xff1a;利用全反射来传输光信号光纤的色散光纤的工作频段光纤的损耗光纤的分类光纤的不同标准及应用光纤的接口类型&#xff08;SC、LC、ST、FC&#xff09;光纤的常见标示方法&#xff1a;如“FC/PC”&a…

JSP之原理剖析

什么是JSP&#xff1a; java Server Pages: java服务端页面,也和Servlet一样&#xff0c;用于动态Web技术&#xff1f; 最大特点&#xff1a; 写jsp就像在写HTML区别&#xff1a; HTML只给用户提供静态的数据JSP页面中可以嵌入Java代码&#xff0c;为用户提供动态数据 JSP原…