【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.mzph.cn/bicheng/13322.shtml

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

相关文章

使用Python实现深度学习模型:自动编码器(Autoencoder)

自动编码器&#xff08;Autoencoder&#xff09;是一种无监督学习的神经网络模型&#xff0c;用于数据的降维和特征学习。它由编码器和解码器两个部分组成&#xff0c;通过将输入数据编码为低维表示&#xff0c;再从低维表示解码为原始数据来学习数据的特征表示。本教程将详细介…

编译gdb:在x86虚拟机上,加载分析arm程序及崩溃

目标 在X86虚拟机上&#xff0c;加载arm程序及崩溃。 最早我想的是编译一个arm版本的&#xff0c;在虚拟机上显然不能使用。 后来同事跟我说&#xff0c;可以编译一个在虚拟机上&#xff0c;分析arm的gdb&#xff0c;我觉得好神奇。事实证明确实可以。 首先不能使用已编译的…

【Maven】属性

Maven中的属性&#xff08;Properties&#xff09;是pom.xml文件中用于存储配置信息的元素。这些属性可以是项目级的、用户级的或者系统级的&#xff0c;并且可以在整个pom.xml文件中通过${属性名}的格式进行引用。Maven属性为配置管理提供了很大的灵活性。 以下是Maven中不同类…

第十七篇:数据库性能优化的数学视角:理论与实践的融合

数据库性能优化的数学视角&#xff1a;理论与实践的融合 1. 引言 在现代信息技术快速发展的背景下&#xff0c;数据库性能优化已经成为计算机科学领域的一个热点问题。随着数据量的爆炸式增长和用户需求的多样化&#xff0c;数据库系统所承载的数据处理任务变得越来越复杂&…

Redis第17讲——Redis zset结构实现滑动窗口限流

一、什么是滑动窗口限流 滑动窗口限流是一种流量控制策略&#xff0c;用于控制在一定时间内允许执行的操作数量或请求频率。它的工作方式类似于一个滑动时间窗口&#xff0c;对每个时间窗口的请求数量进行计数&#xff0c;并根据预先设置的限流策略来限制或调节流量&#xff0…

[muduo网络库]——muduo库InetAddress类(剖析muduo网络库核心部分、设计思想)

接着之前我们[muduo网络库]——muduo库EventLoopThreadPool类&#xff08;剖析muduo网络库核心部分、设计思想&#xff09;&#xff0c;我们接着看完除去TcpServer的最后一个InetAddress类。InetAddress 类是 muduo 网络库中的一个重要类&#xff0c;用于表示网络中的 IP 地址和…

maven deploy项目发布到中央仓库GPG签名失败signing failed: No secret key

maven deploy项目发布到中央仓库GPG签名失败signing failed: No secret key 执行操作 在我执行命令打包项目到中央仓库时失败 mvn clean deploy错误信息 [INFO] --- gpg:3.1.0:sign (sign-artifacts) LocalCache --- [INFO] Signing 4 files with 9961AA14xxxxxxxxxxxxxxD…

Ps 滤镜:彩色铅笔

Ps菜单&#xff1a;滤镜/滤镜库/艺术效果/彩色铅笔 Filter Gallery/Artistic/Colored Pencil 彩色铅笔 Colored Pencil滤镜用于模拟用彩色铅笔手绘的艺术效果&#xff0c;它能够在纯色背景上重新绘制图像&#xff0c;同时保留边缘细节并显示出粗糙的阴影线。此滤镜特别适合用于…

STM32HAL库-中断篇

中断 中断简介 中断是一种事件处理机制&#xff0c;可以暂停主程序的运行&#xff0c;转而处理特定事件程序。 中断的作用和意义&#xff1a; 实时控制 在确定事件内对响应事件做出相应 故障处理 检测到故障需要第一时间处理 数据传输 如串口通信&#xff0c;不确定数…

cgicc开发 (结合jsoncpp)

#include <iostream> #include <fstream> //读写文件 c标准库 #include <string> //字符串类 c标准库 #include <sstream> //字符串流 c标准库 #include <assert.h> #include "json/json.h" //jsoncpp的头文件#include <cgicc/CgiD…

Java基础(37)XSS攻击、SQL注入攻击、CSRF攻击

XSS攻击&#xff08;跨站脚本攻击&#xff09; 定义&#xff1a;XSS&#xff08;Cross-Site Scripting&#xff09;攻击是指攻击者在目标网站上注入恶意的客户端脚本&#xff0c;当其他用户浏览该网站时&#xff0c;嵌入在网页中的这段脚本会被执行&#xff0c;从而达到攻击的…

<sa8650>QCX Usecase 使用详解—拓扑图 XML 定义

<sa8650>QCX Usecase 使用详解—拓扑图 XML 定义 一 、前言二、拓扑图 XML 定义2.1 <Node, port, link>2.2 < XML prolog >2.3 < UsecaseDef >2.4 < Usecase>2.5 < Targets>2.5.1 < Target>2.5.2 < Range>2.6 < Pipeline>2.…

C++之lambda【匿名函数】

1、语法 语法结构&#xff1a; [捕获列表](参数列表) mutable(可选) 异常属性 -> 返回类型 {// 函数处理 }注意&#xff1a; 一般情况下&#xff0c;编译器可以自动推断出lambda表达式的返回类型&#xff0c;所以我们可以不指定返回类型。 但是如果函数体内有多个return语…

维修ABB示教器主板DSQC679 3HAC 033624-001 /R机器人液晶显示屏

ABB 全面的 6 轴关节型机器人产品组合为物料搬运、机器维护、点焊、弧焊、切割、组装、测试、检查、分配、研磨和抛光应用提供了理想的解决方案。 ABB 的协作机器人适用于各种规模的操作中的各种任务。它们易于设置、编程、操作和扩展。由行业领先的专家打造。并由业内最广泛的…

Nacos如何实现负载均衡?

作为一名资深的架构师&#xff0c;我深知在微服务架构中&#xff0c;负载均衡是确保系统高可用性、可扩展性和性能的关键技术之一。Nacos作为一款动态服务发现、配置和服务管理平台&#xff0c;为微服务架构中的负载均衡提供了强大的支持。接下来&#xff0c;我将结合我的实践经…

速盾:cdn加速技术原理

CDN&#xff08;Content Delivery Network&#xff09;加速技术是一种基于分布式部署的网络加速方案&#xff0c;旨在提高用户访问网页或者应用程序的响应速度和稳定性。它通过将内容缓存在离用户最近的边缘节点上&#xff0c;实现就近访问&#xff0c;从而减少了传输延迟和网络…

584. 寻找用户推荐人

584. 寻找用户推荐人 题目链接&#xff1a;584. 寻找用户推荐人 代码如下&#xff1a; # Write your MySQL query statement below select name from Customer where referee_id is null or referee_id<>2;

Mamba:7 VENI VIDI VICI

若在阅读过程中有些知识点存在盲区&#xff0c;可以回到如何优雅的谈论大模型重新阅读。另外斯坦福2024人工智能报告解读为通识性读物。若对于如果构建生成级别的AI架构则可以关注AI架构设计。技术宅麻烦死磕LLM背后的基础模型。 序列模型的效率与有效性之间的权衡取决于状态编…

Android动画与视图绘制流程的关系

Android动画主要分为三种&#xff1a;帧动画、View动画&#xff08;补间动画&#xff09;、属性动画。每种动画的实现原理和它们与视图绘制流程&#xff08;测量、布局和绘制&#xff09;之间的关系如下&#xff1a; 1. 帧动画&#xff08;Frame Animation&#xff09; 帧动画…

实锤,阿里云盾会拦截百度云防护的IP!

今天凌晨&#xff0c;一位站长联系上云加速客服&#xff0c;反馈说&#xff0c;网站突然出现了502的情况。 在检查云防护子域名配置没有问题、本地强制回源没有问题的情况下&#xff0c;我们得出结论是要么服务器内防火墙拦截了云防护的IP段&#xff0c;要么服务器商拦截了云防…