【C语言深度解剖】(12):C语言库函数的学习和模拟实现,一篇文章就够了!

🤡博客主页:醉竺

🥰本文专栏:《C语言深度解剖》

😻欢迎关注:感谢大家的点赞评论+关注,祝您学有所成!


✨✨💜💛想要学习更多C语言深度解剖点击专栏链接查看💛💜✨✨ 


目录

0. 前言

1. 函数介绍 

1.1 strlen 

1.3 strcat 

1.4 strcmp 

1.5 strncpy 

1.6 strncat 

1.7 strncmp 

1.8 strstr 

1.9 strtok

1.10 strerror 

1.11 memcpy 

1.12 memmove

1.13 memcmp

1.14 memset 

2. 库函数的模拟实现 

2.1 模拟实现strlen 

2.2 模拟实现strcpy 

2.3 模拟实现strcat

2.4 模拟实现strstr 

2.5 模拟实现strcmp 

2.6 模拟实现memcpy 

2.7 模拟实现memmove 


本章重点

重点介绍处理字符和字符串的库函数的使用和注意事项

字符串长度

  • strlen

长度不受限制的字符串函数

  • strcpy
  • strcat
  • strcmp

长度受限制的字符串函数介绍

  • strncpy
  • strncat
  • strncmp

字符串查找

  • strstr
  • strtok

错误信息报告

  • strerror

字符操作

内存操作函数

  • memcpy
  • memmove
  • memset
  • memcmp 

0. 前言

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串中或者字符数组中。

字符串常量 适用于那些对它不做修改的字符串函数。


1. 函数介绍 

1.1 strlen 

size_t strlen(const char* str);
  • 字符串已'\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。
  • 参数指向的字符串必须要以 '\0' 结束。
  • 注意函数的返回值为size_t,是无符号的( 易错 ) 
  • 学会strlen函数的模拟实现

1.2 strcpy 

1.3 strcat 

将源字符串的副本附加到目标字符串。目标字符串中的终止空字符 '\0' 被源字符串的第一个字符覆盖,并返回目标字符串的起始地址。

1.4 strcmp 

int strcmp ( const char * str1, const char * str2 );

strcmp 函数比较两个字符串 s1 和 s2,按照字典顺序(即 ASCII 值的大小)进行比较。比较过程从两个字符串的第一个字符开始,直到遇到一个不同的字符或者达到其中一个字符串的末尾。 

  • 如果 s1 和 s2 相等,返回 0。
  • 如果 s1 小于 s2(即 s1 的第一个不匹配字符的 ASCII 值小于 s2 对应字符的 ASCII 值),返回一个小于 0 的值。
  • 如果 s1 大于 s2(即 s1 的第一个不匹配字符的 ASCII 值大于 s2 对应字符的 ASCII 值),返回一个大于 0 的值。 

1.5 strncpy 

它的作用是将源字符串的一部分复制到目标字符串中,并且可以指定复制的最大字符数。 

  • dest:指向目标字符串的指针,目标字符串必须有足够的空间来保存复制的字符以及终止符 '\0'。
  • src:指向源字符串的指针,该字符串的一部分将被复制到目标字符串中。
  • n:要复制的最大字符数。如果 src 的长度小于 n,则 dest 中的剩余空间将填充为 '\0';如果 src 的长度大于或等于 n,则只有前 n 个字符会被复制到 dest 中。 

使用 strncpy 函数时,需要注意的是:

  1. 如果 src 的长度小于 n,dest 中剩余的空间将被填充为 '\0',确保目标字符串以终止符结束。
  2. 如果 src 的长度大于或等于 n,dest 不会自动添加终止符 '\0',这可能导致 dest 不是有效的 C 字符串。
  3. strncpy 不会检查 dest 的空间是否足够,因此在使用前应该确保 dest 有足够的空间。

1.6 strncat 

将源字符串的前num个字符以及一个终止的空字符追加到目标字符串。

char *strncat(char *dest, const char *src, size_t n);
  • dest:指向目标字符串的指针,目标字符串必须有足够的空间来保存附加的字符以及终止符 '\0'。
  • src:指向源字符串的指针,该字符串的一部分将被附加到目标字符串的末尾。
  • n:要附加的最大字符数。如果 src 的长度小于 n,则整个 src 会被附加到 dest;如果 src 的长度大于或等于 n,则只有前 n 个字符会被附加到 dest 中,并且 strncat 会自动在末尾添加终止符 '\0'。 

使用 strncat 函数时,需要注意的是:

  • 目标字符串 dest 必须有足够的空间来保存附加的字符以及终止符 '\0',否则可能会导致缓冲区溢出。
  • 目标字符串 dest 必须以空字符 '\0' 结尾,因为 strncat 函数会从 dest 的空字符开始附加 src。
  • strncat 不会检查 dest 的空间是否足够,因此在使用前应该确保 dest 有足够的空间。

1.7 strncmp 

int strncmp ( const char * str1, const char * str2, size_t num );

它的作用是比较两个字符串的前 num个字符,根据比较结果返回一个整数值。

  • s1:指向第一个字符串的指针。
  • s2:指向第二个字符串的指针。
  • n:要比较的最大字符数。 

 strncmp 函数比较两个字符串 str1 和 str2 的前 num 个字符,按照字典顺序(即 ASCII 值的大小)进行比较。比较过程从两个字符串的第一个字符开始,直到遇到一个不同的字符、达到 num 个字符或者达到其中一个字符串的末尾。

strncmp 函数的返回值如下:

  • 如果 str1 和 str2 的前 n 个字符相等,返回 0。
  • 如果 s1 的前 num 个字符小于 str2 的前 num 个字符(即 str1 的某个字符的 ASCII 值小于 str2 对应字符的 ASCII 值),返回一个小于 0 的值。
  • 如果 str1 的前 num 个字符大于 str2 的前 num 个字符(即 str1 的某个字符的 ASCII 值大于 str2 对应字符的 ASCII 值),返回一个大于 0 的值。

1.8 strstr 

char * strstr ( const char *str1, const char * str2);

它的作用是在一个字符串 str1 中查找子字符串 str2 的第一次出现。如果找到子字符串,strstr 函数返回指向 str1 中子字符串第一次出现的指针;如果没有找到,返回 NULL。 

  • str1:指向要搜索的字符串的指针。
  • str2:指向要查找的子字符串的指针 

请注意,strstr 函数区分大小写,因此在搜索时它会考虑字符的大小写。如果你需要进行不区分大小写的搜索,你可能需要使用自定义函数或者库提供的其他函数。 

1.9 strtok

char * strtok ( char * str, const char * sep );

  • sep参数是个字符串,定义了用作分隔符的字符集合。
  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标 记。
  • strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)
  • strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串 中的位置。
  • strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。
  • 如果字符串中不存在更多的标记,则返回 NULL 指针。

1.10 strerror 

char * strerror ( int errnum );
  • errnum:一个整数,表示错误码。 

strerror 函数是 C 语言标准库中的一个函数,用于将错误码转换为错误消息字符串。它的作用是根据错误码 errnum 返回一个指向描述该错误的消息的指针。 


字符分类函数: 

函数如果他的参数符合下列条件就返回真
iscntrl任何控制字符
isspace空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
isdigit十进制数字 0~9
isxdigit十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower小写字母a~z
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
isprint任何可打印字符,包括图形字符和空白字符

 字符转换:

1.11 memcpy 

void *memcpy(void *dest, const void *src, size_t n);
  • 从源内存地址 src 开始拷贝 n 个字节到目标内存地址 dest。 
  • dest 是指向用于存储拷贝内容的目标数组的指针。
  • src 是指向要拷贝的数据源的指针,src 和 dest 的类型通常是一个无类型指针(void pointer),这意味着它们可以指向任何类型的数据。
  • memcpy 拷贝指定数量的字节,不会在目标数组中添加空字符,也不会因为遇到空字符而停止拷贝。
  • n 是一个 size_t 类型的值,代表要拷贝的字节数。 

使用 memcpy 函数时需要小心,因为它不检查数组边界或重叠。如果目标和源内存区域重叠,拷贝的结果是未定义的。如果需要处理可能重叠的内存区域,应该使用 memmove 函数。 

在这个例子中,字符串 "Hello World" 被复制到 dest 数组中,包括字符串结束的空字符。函数返回的是 dest 的指针,但在实际使用中通常不需要接收这个返回值。 

1.12 memmove

void * memmove ( void * destination, const void * source, size_t num );
  • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的
  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理

在这个例子中,memmove将字符串 “can be very” 从索引15开始的位置复制到索引20开始的位置,覆盖了 “can be very” 后面的字符。最终打印的字符串将是 “memmove can be very useful”。

请注意,memmove和 memcpy的功能类似,但是 memcpy不保证在源地址和目标地址重叠时能正确工作,而 memmove可以。因此,当你不确定源地址和目标地址是否重叠时,应该使用 memmove来确保程序的正确性。

虽然 memmove 可以处理重叠的内存区域,但如果您确定内存区域不会重叠,使用 memcpy 函数会更高效,因为 memcpy 不会处理重叠的情况,因此通常执行速度更快。

1.13 memcmp

int memcmp(const void *ptr1, const void *ptr2, size_t num);

memcmp 是 C 语言标准库中的一个函数,用于比较两个内存块中的前 num 个字节。它按字节逐个比较两个内存块,直到遇到不同的字节或者比较完指定的字节数。

参数说明:

  • ptr1:指向第一个内存块的指针。
  • ptr2:指向第二个内存块的指针。
  • num:要比较的字节数。

返回值:

  • 如果 ptr1 和 ptr2 指向的前 num 个字节都相同,memcmp 返回 0。
  • 如果 ptr1 指向的字节大于 ptr2 指向的字节,memcmp 返回一个正数。
  • 如果 ptr1 指向的字节小于 ptr2 指向的字节,memcmp 返回一个负数。

1.14 memset 

void *memset(void *ptr, int value, size_t num);

memset 函数会将 ptr 指向的内存区域的前 num 个字节设置为 value 的值。这个函数通常用于初始化内存块,或者将内存块中的数据清零。函数的返回值是指向被设置内存区域的指针,即 ptr。 


2. 库函数的模拟实现 

2.1 模拟实现strlen 

三种方式:

方式1:计数器方式

int my_strlen(char* str)
{
	int length = 0;

	while (*str != '\0')
	{
		length++;
		str++;
	}

	return length;
}

方法2:(不能创建临时变量计数器)递归

int my_strlen(char* str)
{
	if (*str == '\0')
		return 0;
	else
		return 1 + my_strlen(str + 1);
}

方法3:指针-指针的方式 

int my_strlen(char* str)
{
	char* start = str;
	while (*str != '\0')
	{
		str++;
	}

	return str - start;
}
int my_strlen(char* str)
{
	char* start = str;
	while (*str++ != '\0')
	{
		;
	}

	return str - start - 1;
}

2.2 模拟实现strcpy 

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

2.3 模拟实现strcat

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}

	return *str1 - *str2;

	/*if (*str1 > *str2)
		return 1;
	else
		return -1;*/
}

2.4 模拟实现strstr 

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	char* s1 = NULL;
	char* s2 = NULL;
	char* cp = (char*)str1;

	while (*cp)
	{
		s1 = cp;
		s2 = (char*)str2;
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}

		if (*s2 == '\0')
			return cp;
		cp++;
	}
	return NULL;
}

2.5 模拟实现strcmp 

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}

	return *str1 - *str2;

	/*if (*str1 > *str2)
		return 1;
	else
		return -1;*/
}
int my_strcmp(const char* src, const char* dst)
{
    int ret = 0;
    assert(src != NULL);
    assert(dest != NULL);
    while (!(ret = *(unsigned char*)src - *(unsigned char*)dst) && *dst)
        ++src, ++dst;
    if (ret < 0)
        ret = -1;
    else if (ret > 0)
        ret = 1;
    return(ret);
}

2.6 模拟实现memcpy 

void* my_memcpy(void* dest, void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);

	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}

2.7 模拟实现memmove 

void* my_memove(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);
		}
	}
	return ret;
}

 到这里C语言库函数的学习基本就结束了~如果本篇文章对您有帮助,麻烦点赞收藏以及评论一下把~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python REST 请求验证

REST 请求验证 在Python中&#xff0c;可以使用cerberus库来进行REST请求的验证。以下是一个简单的示例&#xff0c;演示如何使用cerberus进行请求数据的验证。 首先&#xff0c;安装cerberus库&#xff1a; pip install cerberus # 安装cerberus库 -- Anaconda 环境下安装 …

非成对意象翻译中的内容制约范式再思考

Rethinking the Paradigm of Content Constraints in Unpaired Image-to-Image Translation 非成对意象翻译中的内容制约范式再思考 Xiuding Cai1 2, Yaoyao Zhu1 2, Dong Miao1 2, Linjie Fu1 2, Yu Yao1 2 蔡秀定 1 2 、朱瑶瑶 1 2 、苗东 1 2 、付林杰 1 2 、余瑶 1 2 Corre…

什么是直接内存(NIO)

直接内存不受 JVM 内存回收管理&#xff0c;是虚拟机的系统内存&#xff0c;常见于 NIO 操作时&#xff0c;用于数据 缓冲区&#xff0c;分配回收成本较高&#xff0c;但读写性能高&#xff0c;不受 JVM 内存回收管理。 举例 当上传一个较大文件&#xff08;200M&#xff09;…

模版方法详解

模板方法模式 1 概述 在面向对象程序设计过程中&#xff0c;程序员常常会遇到这种情况&#xff1a;设计一个系统时知道了算法所需的关键步骤&#xff0c;而且确定了这些步骤的执行顺序&#xff0c;但某些步骤的具体实现还未知&#xff0c;或者说某些步骤的实现与具体的环境相…

RT-Thread中使用Mqtt

环境&#xff1a; 开发板&#xff1a;Panduola&#xff08;stm32L475&#xff09; KEIL5 开发环境 rtthread 4.0.3内核 使用ENV 配置Rtt MQTT 1.MQTT介绍 ​ 客户端 Client 使用MQTT的程序或设备。客户端总是通过网络连接到服务端。它可以发布应用消息给其它相关的客户端。订…

Python代码:三、读入字符串

1、题目 从变量输出开始。请使用input函数读入一个字符串&#xff0c;然后将其输出。 2、代码 import sys ainput() print(a) 3、在sublime运行的结果

客观需求验证的常见5大步骤(实施版)

我们在挖掘用户需求时&#xff0c;往往容易犯伪需求或需求错位等问题&#xff0c;因此需要进行客观需求验证。通过客观的验证&#xff0c;我们可以有效减少主观判断误差问题&#xff0c;确保需求的准确性&#xff0c;从而降低需求变更和项目风险的概率&#xff0c;减少开发成本…

intel三年来首次大更新竟然倒吸牙膏,线程数砍掉25%!

每年科技圈最热闹的几个话题&#xff0c;无非是几大科技公司发布新的产品&#xff0c;那这其中必然有核心巨头 intel 的身影。 据外媒 Benchlife 披露&#xff0c;英特尔计划在其 Arrow Lake-S 架构 Core Ultra 200 台式机 CPU 系列中推出共计 21 款 CPU。 这是 intel 首次在桌…

白酒:酒精度数对白酒风味的影响与品鉴技巧

云仓酒庄豪迈白酒作为品质的白酒品牌&#xff0c;其酒精度数对白酒风味的影响与品鉴技巧是品鉴爱好者关注的重点。酒精度数作为衡量白酒质量的一项重要指标&#xff0c;不仅决定了白酒的口感和风格&#xff0c;更在一定程度上体现了白酒的品质和价值。本文将探讨酒精度数对云仓…

Jmeter中线程组介绍

1.线程数的意义 Jmeter采用了线程来模拟用户&#xff0c;即1个线程代表1个用户&#xff0c;线程可以简单理解为计算机处理任务时的一个具体执行人。 一个任务可以由多个人&#xff08;线程&#xff09;共同完成&#xff0c;也可以由一个人&#xff08;线程&#xff09;来完成&a…

WebLogic SSL应用

SSL 安全套接字层(SSL)是通过在客户端和Web服务器端之间进行身份验证,并对双方交换的数据进行加密,从而提供安全连接。 验证类型: 单向:客户端验证Web服务器端证书 双向:客户端验证Web服务器证书, Web服务器验证客户端证书 Weblogic Server12c 支持 SSL 3.0 和 TLS1.0 …

智能防疫电梯模拟控制系统设计-设计说明书

设计摘要&#xff1a; 本设计是基于单片机的智能防疫电梯模拟控制系统&#xff0c;主要实现了多项功能。首先&#xff0c;系统进行无接触测温&#xff0c;如果温度正常则可以启动电梯运行&#xff0c;如果温度异常则电梯会报警提示有乘客体温异常&#xff0c;电梯不会运行。其…

Java | Leetcode Java题解之第88题合并两个有序数组

题目&#xff1a; 题解&#xff1a; class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {int p1 m - 1, p2 n - 1;int tail m n - 1;int cur;while (p1 > 0 || p2 > 0) {if (p1 -1) {cur nums2[p2--];} else if (p2 -1) {cur nums1[p…

使用单目相机前后帧特征点匹配进行3D深度估计的方法

在计算机视觉和机器人领域&#xff0c;三维空间感知是实现环境理解和交互的核心技术之一。特别是在资源受限的场合&#xff0c;使用针孔模型的单目相机进行深度估计成为了一种既经济又实用的解决方案。单目深度估计技术依赖于从连续视频帧中提取和匹配特征点&#xff0c;以估计…

dbeaver 链接 Oceanbase 数据库,dbeaver安装数据库驱动

新增驱动 提前到Oceanbase官网下载好驱动 1、点击数据库 -> 驱动管理器 -> 新建 2、添加驱动文件 联接数据库 1、选择你添加的驱动 2、测试

CST电磁仿真软件远场变更和结果相关【从入门到精通】

1、使用阵列系数计算阵列远场结果 对单一天线进行 仿真分析后&#xff0c;查看反映阵列系数的远场结果&#xff01; Navigation Tree > Farfields > Selection > Farfield Plot > Array Factor 下面介绍一下&#xff0c;对单一天线进行仿真后&#xff0c;轻松计…

小白必看:新手学编程必会的100个代码

前言 我记得刚开始接触编程的时候&#xff0c;觉得太难了。 也很好奇&#xff0c;写代码的那些人也太厉害了吧&#xff1f;全是英文的&#xff0c;他们的英文水平一定很好吧&#xff1f; 他们是怎么记住这么多代码格式的&#xff1f;而且错了一个标点符号&#xff0c;整个程…

线性模型之岭回归的用法

实战&#xff1a;使用岭回归模型 完整代码&#xff1a; import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression from sklearn.datasets import make_regression from sklearn.model_selection import train_test_split fro…

C语言收尾 预处理相关知识

一. 预处理详解 1.1 预定义符号 FILE //进行编译的源文件LINE //文件当前的行号DATE //文件被编译的日期TIME //文件被编译的时间FUNCTION //文件当前所在的函数STDC //如果编译器遵循ANSI C标准&#xff0c;其值为1&#xff0c;否则未定义 这些预定义符号都是语言内置的 我们…

【教学类-55-04】20240515图层顺序挑战(四格长条纸加黑色边框、4*4、7张,不重复5400张,16坐标点颜色哈希值去重、保留7色)

背景需求&#xff1a; 前文实现了7张色彩纸条加上黑色边框的需求。 【教学类-55-02】20240512图层顺序挑战&#xff08;四格长条纸加黑色边框、4*4、7张 、43200张去掉非7色有23040张&#xff0c;哈希算法快速去重剩余1221张&#xff09;-CSDN博客文章浏览阅读1k次&#xff0…