【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;elsereturn 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;elsereturn -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;elsereturn -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语言库函数的学习基本就结束了~如果本篇文章对您有帮助,麻烦点赞收藏以及评论一下把~

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

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

相关文章

界面组件DevExpress Reporting v24.1预览版 - 拥有原生Angular报表查看器

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 下一个主要更新(v24.1)将于6月初发布&#xff…

echarts实现金价可视化大屏(项目实战)

前言 最近由于某种原因参加了一个比赛&#xff0c;三天时间肝出来一个可视化大屏项目&#xff08;无后端&#xff09;&#xff0c;代码已开源&#xff0c;但是还在比赛期间不知道会不会影响到 技术使用&#xff1a;html&#xff0c;css&#xff0c;js&#xff0c;echarts&#…

机器学习中10种损失函数大梳理!建议收藏,你一定用得到

接上篇&#xff1a;机器学习中10种损失函数大梳理&#xff01;建议收藏&#xff0c;你一定用得到-CSDN博客 8、希尔伯特-施密特口袋 希尔伯特-施密特口袋&#xff08;Hinge-Schmidt Pocket&#xff09;是一种用于支持向量机训练的损失函数。它是一种改进的希尔伯特-施密特损失…

Qwen 开源标杆

Qwen的博客 在线体验Qwen开源版本、非常丝滑 不算量化、已经开源的Qwen1.5 版本有9个&#xff1a; 0.5B、1.8B、4B、7B、14B、32B、72B、110B、MoE-A2.7B 闭源已经发展到 Qwen-Max-0428、网页端从2.1升级到2.5 Qwen API详情 一些记录&#xff1a; 1、Qwen1.5 110B&#x…

echart树状图图表

根据后端返回的树状结构&#xff0c;渲染echart树状图&#xff0c;并且默认二级节点后是折叠的 父级引入组件 .tree_chart {width: 100%;height: calc(100% - 30px);}<div class"tree_chart"><EachartTree :treeData"allData.treeData" /><…

机试:字符串相关简单问题

字符移动问题 这道题的描述是这样的&#xff1a;输入一个字符串&#xff0c;将其中的数字字符移动到非数字字符之后&#xff0c;并保持数字字符和非数字字符输入时的顺序。例如&#xff1a;输入字符串“ab4f35gr#a6”,输出为“abfgr#a4356”。 以下使我试着敲的代码&#xff…

STK中的光照计算模型

本文简要阐述STK中光照计算的模型。 在航天任务中&#xff0c;通常需要分析地面站、飞行器在一定时间内的光照情况&#xff0c;具体包括&#xff1a; 地面站处在光照区和阴影区的具体时间范围&#xff1b;考虑地形遮挡后&#xff0c;地面站的光照区和阴影区的变化情况&#x…

SketchUp Pro 2024 mac草图大师 激活版 专业的3D建模软件

对于追求专业、高效的设计师们来说&#xff0c;SketchUp Pro 2024 for Mac无疑是最佳的选择。它提供了线条、形状、曲线、文本和图像等多种建模元素&#xff0c;让您能够根据需求自由创作。同时&#xff0c;软件还支持智能标注和尺寸功能&#xff0c;让建模过程更加精确、高效。…

加盟商小程序运用线上渠道的作用是什么

很多行业都有头部品牌&#xff0c;为了满足市场需要和品牌发展、提升营收&#xff0c;加盟模式很常见&#xff0c;通过不断宣传/品牌影响力&#xff0c;获得更多有意向的人&#xff0c;而有意向的用户也需要找到靠谱的品牌及清晰流程等。 在【雨科】平台可制作加盟商信息服务展…

通俗易懂讲乐观锁与悲观锁

浅谈乐观锁与悲观锁 乐观锁和悲观锁是Java并发编程中的两个概念。使用乐观锁和悲观锁可以解决并发编程中数据不一致性、死锁、性能差等问题&#xff0c;乐观锁与悲观锁的实行方式不同&#xff0c;所以其特性也不近相同&#xff0c;下文将详细介绍两者的特性与适用场景。 《熊…

MinIO学习笔记

MinIO学习笔记 MinIO简介Springboot整合MinioMinIO中的Bucket、ObjectMinioClient的常用API&#xff1a;操作bucketMinioClient的常用API&#xff1a;操作对象 MinIO集群部署纠删码模式部署单机多磁盘的纠删码模式部署分布式集群部署 Nginx 视频学习地址 MinIO简介 MINIO干什…

机器人非线性系统反馈线性化与解耦

机器人非线性系统的反馈线性化和解耦是控制理论中的两个重要概念&#xff0c;它们分别用于简化系统分析和设计过程&#xff0c;提高控制系统的性能。 首先&#xff0c;反馈线性化是一种将非线性系统转化为线性系统的技术。在机器人控制中&#xff0c;由于机器人本身是一个强耦…