字符串函数以及内存函数的模拟实现(超详细,全面理解字符串函数!!!)

news/2024/5/22 3:56:41/文章来源:https://blog.csdn.net/m0_72161237/article/details/126897136

目录

一、strlen

1.参数指向的字符串必须要以 '\0' 结束。

2.注意strlen函数的返回值为size_t,是无符号的

3.模拟实现strlen

 二、strcpy

1.源字符串中的 '\0' 拷贝到目标空间

2.源字符串必须以 '\0' 结束

3.目标空间必须足够大,以确保能存放源字符串

4.模拟实现strcpy

三、strcat

 1.源字符串必须以 '\0' 结束

2.模拟实现strcat

四、strcmp

1.标准规定

 2.模拟实现strcmp

五、小结

六、strncpy

 七、strncat

八、 strncmp

 九、strstr

9.1模拟实现strstr

 十、strtok

十一、strerror

十二、字符分类函数: 

十三、memcpy

 1.memcpy:内存拷贝

 2.模拟实现memcpy

十四、memmove

十五、memset


一、strlen

strlen:求字符串长度的,统计的是字符串中\0之前出现的字符个数(不包含 '\0' )

1.参数指向的字符串必须要以 '\0' 结束。

int main()
{//a b c \0 d e f \0char arr[] = "abc\0def";printf("%d\n", strlen(arr));return 0;
}

此结果输出为  3(\0结束

2.注意strlen函数的返回值为size_t,是无符号

#include <string.h>
int main()
{if (strlen("abc") - strlen("abcdef") > 0)printf(">\n");elseprintf("<\n");return 0;
}

对于此代码,大家第一眼看到的肯定是小于,则不然,输出结果为  >

size_t,是无符号的整形(无符号减无符号为无符号),不可能返回负数

3.模拟实现strlen

#include <assert.h>
size_t my_strlen(const char* str)//不会改arr,最好使用const
{assert(str);//保证str为空指针const char* start = str;//记录起始位置const char* end = str;//记录末尾位置while (*end != '\0'){end++;//从a往后走直到指向\0}return end - start;//得到指针个数
}int main()
{char arr[] = "abcdef";int len = my_strlen(arr);printf("%d\n", len);return 0;
}

 运行结果:

 二、strcpy

 strcpy:字符串拷贝

1.源字符串中的 '\0' 拷贝到目标空间

#include <string.h>int main()
{char arr[10] = "xxxxxxxxxx";const char* p = "abcdef";strcpy(arr, p);printf("%s\n", arr);return 0;
}

2.源字符串必须以 '\0' 结束

2.1  没有‘ \0

int main()
{char arr[10] = "xxxxxxxxx";char arr2[] = { 'b', 'i', 't'};strcpy(arr, arr2);printf("%s\n", arr);return 0;
}

 1.2有‘ \0

#include <stdio.h>
int main()
{char arr[10] = "xxxxxxxxx";char arr2[] = { 'b', 'i','\0', 't'};;strcpy(arr, arr2);printf("%s\n", arr);return 0;
}

3.目标空间必须足够大,以确保能存放源字符串

#include <string.h>
int main()
{char arr[3] = {0};char arr2[] = "abcdef";strcpy(arr, arr2);printf("%s\n", arr);return 0;
}

4.模拟实现strcpy

char* my_strcpy(char* dest, const char* src)
{assert(dest);//断言,保证有效性assert(src);char* ret = dest;while (*dest++ = *src++){;}return ret;
}int main()
{char arr1[20] = "abc";char arr2[] =   "hello bit";printf("%s\n", my_strcpy(arr1, arr2));return 0;
}

三、strcat

 strcat:字符追加函数

#include <string.h>
int main()
{char arr1[20] = "hello ";char arr2[] = "world";strcat(arr1, arr2);//字符追加函数printf("%s\n", arr1);return 0;
}

 1.源字符串必须以 '\0' 结束

2.模拟实现strcat

#include <string.h>
#include <stdio.h>
char* my_strcat(char* dest, const char*src)
{//1.找目标空间中的\0char* cur = dest;while (*cur){cur++;}//2.拷贝源头数据到\0之后的空间while (*cur++ = *src++){;}return dest;
}int main()
{char arr1[20] = "hello \0xxxxxxxxxx";char arr2[] = "world";printf("%s\n", my_strcat(arr1, arr2));return 0;
}

 

四、strcmp

 strcmp:字符串比较(比较的是对应位置上字符的大小,而非长度必须以 '\0' 结束

1.标准规定

第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字

 2.模拟实现strcmp

#include <stdio.h>
#include <string.h>
#include <assert.h>int my_strcmp(const char* s1, const char* s2)
{assert(s1 && s2);while (*s1 == *s2){if (*s1 == '\0'){return 0;}s1++;s2++;}return *s1 - *s2;
}int main()
{char arr1[] = "abc";char arr2[] = "abc";int ret = my_strcmp(arr1, arr2);if (ret < 0)printf("arr1<arr2\n");else if(ret>0)printf("arr1>arr2\n");elseprintf("arr1==arr2\n");printf("%d\n", ret);return 0;
}

五、小结

长度不受限制的字符串:

strcpy

strcat

strcmp

长度受限制的字符串:

strncpy

strncat

strncmp

六、strncpy

#include <stdio.h>
#include <string.h>int main()
{char arr1[20] = "abcdefghi";char arr2[] = "xxxx";strncpy(arr1, arr2, 2);printf("%s\n", arr1);return 0;
}

 

 假设:拷贝大于arr的空间

 七、strncat

#include <stdio.h>
#include <string.h>int main()
{char arr1[20] = "abcdef\0qqqqqq";char arr2[] = "xyz";strncat(arr1, arr2, 2);printf("%s\n", arr1);return 0;
}

 

八、 strncmp

 

#include <stdio.h>
#include <string.h>int main()
{int ret = strncmp("abcdef", "abc", 3);printf("%d\n", ret);return 0;
}

假设比较前四个就是d\0比较:

 九、strstr

strstr在一个字符串中找另一个字符串是否存在

存在:返回子串第一次出现的位置

不存在:返回NULL

int main()
{char arr1[] = "abcdefabcdef";char arr2[] = "cdq";char* p = strstr(arr1, arr2);if (p == NULL){printf("不存在\n");}else{printf("%s\n", p);}return 0;
}

9.1模拟实现strstr

char* my_strstr(const char* str1, const char* str2)
{const char* s1 = str1;const char* s2 = str2;const char* p = str1;if (*str2 == '\0'){return str1;}while (*p){s1 = p;s2 = str2;while (*s1 != '\0' && *s2 != '\0' && (*s1 == *s2)){s1++;s2++;}if (*s2 == '\0'){return (char*)p;//找到了}p++;}return NULL;//找不到子串
}int main()
{char arr1[] = "abcdefabcdef";char arr2[] = "cdq";char* p = strstr(arr1, arr2);if (p == NULL){printf("不存在\n");}else{printf("%s\n", p);}return 0;
}

 十、strtok

sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。 

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

#include <stdio.h>
#include <stdio.h>int main()
{char arr[] = "zpengwei@bitedu.com";char buf[200] = { 0 };//"zpengwei@bitedu.com"strcpy(buf, arr);const char* p = "@.";char* str = strtok(buf, p);printf("%s\n", str);str = strtok(NULL, p);printf("%s\n", str);str = strtok(NULL, p);printf("%s\n", str);//"@."//strtok();//zpengwei//bitedu//comreturn 0;
}

 优化:

#include <stdio.h>
#include <stdio.h>int main()
{char arr[] = "zpengwei@bitedu.com";char buf[200] = { 0 };//"zpengwei@bitedu.com"strcpy(buf, arr);const char* p = "@.";char* str = NULL;for (str=strtok(buf, p); str!=NULL; str=strtok(NULL, p)){printf("%s\n", str);}return 0;
}

十一、strerror

strerror:把错误码转换成错误信息

#include <stdio.h>
#include <stdio.h>int main()
{printf("%s\n", strerror(0));printf("%s\n", strerror(1));printf("%s\n", strerror(2));printf("%s\n", strerror(3));printf("%s\n", strerror(4));return 0;
}

	//错误码记录到错误码的变量中//errno - C语言提供的全局的错误变量//#include <errno.h>FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("");//打印的依然是errno变量中错误码对应的错误信息//printf("%s\n", strerror(errno));return 1;}//读文件fclose(pf);pf = NULL;return 0;
}

十二、字符分类函数: 

具体用法可以搜索:cplusplus

里边都有详细介绍

十三、memcpy

一组内存函数:

memcpy

memcmp

memmove

memset

 1.memcpy内存拷贝

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。

int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };memcpy(arr2, arr, 20);//float arr1[] = { 1.0f,2.0f,3.0f,4.0f };//float arr2[5] = { 0.0 };//memcpy(arr2, arr1, 8);return 0;
}

 2.模拟实现memcpy

//   memcpy模拟实现#include <assert.h>void* my_memcpy(void* dest, void* src, size_t num)//返回类型 viod*
{void* ret = dest;assert(dest);//断言,不能为NULLassert(src);while(num--)//一次搞定一个字节,一共num个字节{*(char*)dest = *(char*)src;//viod*的指针不能直接引用,强制类型转换成char*的指针dest = (char*)dest + 1;//同理,强制类型转换成char*的指针src = (char*)src + 1;//不能用 (char*)src++ :++在外边}return ret;//不能返回dest,已经不是起始位置,定义ret
}
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };memcpy(arr1+2, arr1, 20);memmove(arr1+2, arr1, 20);int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };my_memcpy(arr2, arr1, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr2[i]);}float arr3[] = { 1.0f,2.0f,3.0f,4.0f };float arr4[5] = { 0.0 };my_memcpy(arr4, arr3, 8);return 0;
}

问题: 把1 2 3 4 5 放到 3 4 5 6 7上,如果使用memcpy时源空间会和目标空间有重合,拷贝的时候可能会把目标空间的一些数据覆盖掉

 需要重新换思路

例如: 

总结:

 则需要memmove函数

十四、memmove

c语言中重叠内存的拷贝是交给:memmove

void* my_memmove(void* dest, void* src, size_t num)
{void* ret = dest;assert(dest);assert(src);if (dest < src)//1 前->后{while(num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}}else //2 3 后->前{while (num--){*((char*)dest + num) = *((char*)src + num);}}return ret;
}memcpy只需要实现不重叠的拷贝就可以了 
memmove是需要实现重叠内存的拷贝的int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };memcpy(arr1+2, arr1, 20);return 0;
}

memcpy只需要实现不重叠的拷贝就可以了 
memmove是需要实现重叠内存的拷贝的

和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。

int main()
{int arr1[] = { 1,2,3,0,5 };//01 00 00 00 02 00 00 00 03 00 00 00 00 00 00 00 ..int arr2[] = { 1,2,3,4,0 };//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ..int ret = memcmp(arr1, arr2, 13);printf("%d\n", ret);return 0;
}

十五、memset

memset:内存设置

测试:

int main()
{int arr[] = { 1,2,3,4,5 };memset(arr, 0, 8);return 0;
}

 

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

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

相关文章

@Conditional

条件装配:满足Conditional指定的条件,则进行组件注入 @Configuration//告诉springboot这是一个配置类 public class MyConfig {@Bean("tom")public Stu stu01(){return new Stu("汤姆");}@ConditionalOnBean(name="tom")//当容器中有tom组件时…

windows工具:推荐一款可以截长图(滚动截图)的工具FSCapture

windows工具&#xff1a;推荐一款可以截长图&#xff08;滚动截图&#xff09;的工具前言一、FSCapture是什么&#xff1f;二、使用方法1.下载地址和安装2.使用方法前言 有的时候你画的框架图太大&#xff0c;已经超过了一屏&#xff0c;想要导出图片&#xff0c;用普通窗口截…

汇编常用寄存器以及寻址方式

寄存器概览 常用寄存器 AX accumulator 累加寄存器 BX base 基址寄存器 CX count 计数寄存器 DX data 数据寄存器 SP stack pointer 堆栈寄存器 BP base pointer 基址指针寄存器 SI source index 源变址寄存器 DI destination index 目的变址寄存器 IP instruction pointer 指…

ch4 报错修正 Sophus使用

ch4 报错& 修正 &#xff08;1&#xff09; # 添加Eigen头文件 include_directories( "/usr/include/eigen3" )&#xff08;2&#xff09; #include "sophus/so3.hpp" #include "sophus/se3.hpp"&#xff08;3&#xff09; 大量报错但都…

定制qga(作业截图)

文章目录一、qga介绍二、证明qga命令可以正常使用三、创建qga安装包四、总步骤一、qga介绍 qemu guest agent简称qga&#xff0c; 是运行在虚拟机内部的一个守护程序&#xff08;qemu-guest-agent.service&#xff09;&#xff0c; 他可以管理应用程序&#xff0c;执行宿主机发…

声呐直线阵正交混频实验(HEU信息与信号处理创新实践项目一)

写在前面 这个实验原要求是要实现 969696 通道的正交混频变换&#xff08;后来老师说只要不是单通道都行&#xff09;&#xff0c;因此必须使用 FIRFIRFIR IP核&#xff08;手搓FIR一两个通道还行&#xff0c;96通道就太费劲了&#xff09;&#xff0c;所以实验成功的关键就是…

BNU002期-学术沙龙-写好综述

文章目录综述的介绍什么是综述为什么要读综述为什么要写综述怎样写综述综述案例中的问题对于综述写作问题的分类如何避免综述写作问题讨论综述问题框架环节并完善做个升华&#xff1a;谈谈科研和读综述的乐趣本文引用资料的链接补充综述的介绍 本文围绕 什么是综述 我创设这…

微服务基础---认识微服务

1.1认识微服务 1.1.1微服务架构演变 单体架构 将业务的所有功能都集中在一个项目中进行开发&#xff0c;打成一个包部署. 优点&#xff1a;架构简单、部署成本低缺点&#xff1a;耦合度高 分布式架构 根据业务功能对系统进行拆分&#xff0c;每个业务模块作为独立项目开发&am…

软件流程和管理(八):Ethics

目录 1. Ethics 1.1 道德&#xff08;Ethics&#xff09;是什么&#xff1f; 1.2 关于计算机伦理的错误假设 1.3 为什么你要关心建立道德技能和知识 1.4 信息技术的道德责任 1.5 澳大利亚计算机协会的道德准则 1.6 组织中的道德是很重要的 1.7 道德&#xff1a;实用指…

zephyr线程生命周期

ephyr中线程是使用CPU的最小单位&#xff0c;线程从创建后由zephyr内核进行调度&#xff0c;根据运行和等待资源的状况在几个状态中切换&#xff0c;直到线程终止退出生命周期。 线程状态 线程在其生命周期中有下面6种状态&#xff1a; New 创建&#xff1a;线程被创建起来但…

实验2:Open vSwitch虚拟交换机实践

(一)基本要求1.ovs-vsctl基础操作实践:创建OVS交换机,以ovs-xxxxxxxxx命名,其中xxxxxxxxx为本人学号。在创建的交换机上增加端口p0和p1,设置p0的端口号为100,p1的端口号为101,类型均为internal;为了避免网络接口上的地址和本机已有网络地址冲突,需要创建虚拟网络空间…

Redis实现消息队列(双端队列的模式,发布订阅模式)

文章目录 1 采用双端队列的模式1.1 入队出队操作1.2 生产者编写1.3 消费者编写1.4 测试2 采用发布订阅模式2.1 编写生产者2.2 编写消费者2.3 测试​ 本部分,我们使用 redis实现消息队列的功能,采用 redis实现消息队列主要有两种方式:采用 redis自带双端队列实现;采用 r…

【牛客刷题-算法】NC7 买卖股票的最好时机(一)

个人主页&#xff1a;清风莫追 系列专栏&#xff1a;牛客刷题——数据结构与算法 推荐一款面试、刷题神器牛客网&#xff1a;&#x1f449;点击开始刷题学习&#x1f448; 文章目录1.题目描述2.算法设计思路3.代码实现4.运行结果结束语&#xff1a;1.题目描述 描述 假设你有一…

Android移动应用开发之ImageView、ProgressBar和Notification的一些简单使用

文章目录主要文件目录MainActivity:NotificationActivitya.pngic_baseline_account_box_24.xmlactivity_main运行主要文件目录 MainActivity: 这里主要用于按钮响应处理和通知处理 package zufe.scq.hunter;import androidx.appcompat.app.AppCompatActivity; import android…

Letcode动态规划专题-困难

10. 正则表达式匹配 42. 接雨水 1.传统方式-按照行的方式计算 整个思路就是&#xff0c;求第 i 层的水&#xff0c;遍历每个位置&#xff0c;如果当前的高度小于 i&#xff0c;并且两边有高度大于等于 i 的&#xff0c;说明这个地方一定有水&#xff0c;水就可以加 11。 如…

pytest测试框架2【控制用例的执行顺序】

1.pytest加载所有的测试用例都是乱序的,如果想指定用例的顺序,可以使用pytest-ordering插件,指定用例的执行顺序只需要在测试用例的方法前面加上装饰器@pytest.mark.run(order=[num])设置order的对应的num值,它就可以按照num的大小顺序来执行 应用场景:有时运行测试用例需…

03(C++二级)

1.函数不可嵌套定义,但可以嵌套调用。 2.静态数据成员必须在类外初始化,使用类名调用。 初始化格式: <数据类型><类名>::<静态数据成员名> = <值> 3.C++不能重载的: :: . * ->* ?: 4.

普通人做网赚在网上依旧是个好机会

有一个朋友和我说&#xff0c;现在网上不怎么好赚钱了&#xff0c;我想去干实体赚钱&#xff0c;听他说这个话&#xff0c;我笑了&#xff0c;实体行业才没有你们所看到的那么简单的&#xff01;作为普通人互联网上搞钱&#xff0c;未来几年依旧会是机会的&#xff01;以前我只…

教程图文详解 - 网络互联与互联网(第六章)

一 前言 多个网络相互连接组成范围更大的网络叫互联网。由于各种网络使用的技术不同&#xff0c;所以网络互联技术就是在不改变原来的网络体系结构的前提下&#xff0c;把一些异构型的网络互相连接构成统一的通信系统&#xff0c;实现更大范围的资源共享。 二 网络互联设备 …

org.springframework.core.annotation.AnnotationUtils.clearCache()V 错误解决(SSM项目)

org.springframework.core.annotation.AnnotationUtils.clearCache 错误解决问题描述解决办法方法一方法二Success问题描述 之前在写了一些 SSM 的项目之后, 自己搭了一个简单的 CRUD邮件注册和登录 的脚手架,前几天运行过一次结果良好, 今天想迁移一部分东西到 SpringBoot 的项…