字符串相关的函数和内存块相关函数

𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇:Solitary-walk

      ⸝⋆   ━━━┓
     - 个性标签 - :来于“云”的“羽球人”。 Talk is cheap. Show me the code
┗━━━━━━━  ➴ ⷯ

本人座右铭 :   欲达高峰,必忍其痛;欲戴王冠,必承其重。

👑💎💎👑💎💎👑 
💎💎💎自💎💎💎
💎💎💎信💎💎💎
👑💎💎 💎💎👑    希望在看完我的此篇博客后可以对你有帮助哟

👑👑💎💎💎👑👑   此外,希望各位大佬们在看完后,可以互赞互关一下,看到必回
👑👑👑💎👑👑👑

 字符串相关的函数和内存块相关函数思维导图:


目录

一:strlen( )

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

        strcpy strcat strcmp

三:长度受限制的字符串函数

       strncpy strncat strncmp

四:字符串查找

       strstr strtok

五:错误信息报告

       strerror

        perror

六:字符分类函数


七:内存函数

memcpy

memmove

memcmp

memset


 一:strlen( )

函数原型:

正确的应用中:strlen()的参数部分必须要有 \0

注意:

1)strlen只统计\0 之前的字符个数

2)strlen的函数返回类型是size_t (对应的占位符是  %u)

 strlen模拟实现

方法1:借助计数的方式

size_t my_strlen(const char* start)
{//采用计数器来模拟实现strlenint count = 0;while (*start ){count++;start++;}return  count;
}
int main()
{printf("%u\n",my_strlen("abc"));}

方法2:借助指针 - 指针 

size_t my_strlen( char* str)
{//采用指针 - 指针实现strlen 模拟char* p = str;//遍历字符串while (*p){p++;}return p - str;
}
int main()
{printf("%u\n",my_strlen("abcd"));
}
二: 长度不受限制的字符串函数
strcpy

函数原型:

使用注意要点:

1)目标空间足够大并且可以修改

2)源字符串必须有 \0

3)  在拷贝的时候 源字符串的\0也拷贝

 strcpy模拟实现

有了以上的注意点,相信对strcpy的模拟实现咱也是易如反掌

思路分析:

1:定义2个指向目标空间和源字符串的指针

 char* dst   //指向目标空间

char*   s     //指向源字符串

2:一个字符一个字符的拷贝

3:注意最后源字符串的\0也要拷贝过去

char* my_strcpy(char* dst, const char* s)
{char* ret = dst;//拷贝完之后返回目标空间起始地址,所以需要先保存一下assert(dst);assert(s);while (*s){*dst++ = *s++;//对源字符串非\0之前的拷贝}//别忘了还有\0没有拷贝*dst = *s;return ret;//返回目标空间起始地址}
int main()
{/*strcpy(char* dst,const char* sor);使用注意事项1:目标空间足够大,源字符串有 \02:目标空间可以修改       const char*dst //err3:源字符串的\0也会拷贝过去*/char a1[20] = {0};char a2[11] = {"hello bit"};my_strcpy(a1, a2);printf("%s\n", a1);return 0;
}

 对于上面这个核心代码还可以进行再次优化

简化版之后的
    while (*dst++ = *s++)  
    {
        ;
         
    }

代码分析:

先把*s赋给 *dst并进行后置加加 注意此时是对加加之前的条件进行判断真假

直到 *s == \0 赋给*dst 结束拷贝同时源字符串的\0也已经拷贝了

strcat

函数原型:

 函数模拟实现

1)首先找到目标空间的\0

2)其次是对源字符串的拷贝 (和strcpy模拟实现一样的)

char* my_strcat(char* dst, const char* s)
{/*先找到目标空间的开始追加的地址(一般是\0)开始拷贝*/char* ret = dst;while (*dst++)  //找 \0{;}while (*dst++ = *s++)//数据拷贝{;}return ret;
}

 是否可以对字符串自己进行追加?

答案:no no no

 分析如下:

假设对字符串 "hello"来进行追加

指针s,dst初始位置

经过依次拷贝之后

通过画图我们知道,此时\0已经被覆盖,这就会导致指针s一直找不到从而造成死循环

strcmp

 strcmp函数介绍:

1)比较对应位置上字符的ASCII码值(a-z的ASCII码值依次增加)

2)函数的返回类型:int 

 strcmp模拟实现

分析:假设比较字符串str1 ,str2

int my_strcmp(const char* s1,const char* s2)
{while (*s1 && *s2){if (*s1 != *s2)break;s1++, s2++;//继续比较下一对字符}return *s1 - *s2;}
三:长度受限制的字符串函数(就是在原来功能的基础上加上了字数的限制)
strncpy

注意当指定拷贝的个数大于源字符串的内容时,会自动补加\0

strncat

 strncat可不像strncpyhan函数一样当指定追加的字符个数大于源字符串的内容时,会自动补加\0

strncmp

注意:对num的传参决定了这个函数返回值

str1: abcd

str2 : abd

当num == 3;返回小于0

当num == 2;返回等于0

当num == 4;返回小于0

四:字符串查找
       strstr

strstr  功能:在一个字符串中查找一个子字符串并返回这个子字符串第一次出现的位置

当 *cp = a时,显然是不匹配的,所以cp指向下一个字符 b,

此时是有一个字符可以匹配上,此时s2++,若继续让cp++,当匹配成功时,cp的初始位置是无法找到的,所以设置一个变量s1,s1++,来到下一个字符b的位置 s1,s2指向的字符相等,此时s1++,s2++二者指向的字符不相等,所以此时cp++指向 

同时s1也指向这里,s1 == s2 ,继续 s1++,s2 ++,

s1 == s2,继续 s1++,s2 ++,

这时候内嵌的while循环结束,执行

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

char* my_strstr( char* str1,  char* str2)
{char* cp = str1;while (*cp){char* s1 = cp;char* s2 = str2;while (*s2 && *s1 && *s1 == *s2)  // 处理对应位置相等{s1++;s2++;}if (*s2 == '\0') return cp;cp++;}//来到这:没有找到return NULL;}
 strtok

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

1)sep参数是个字符串,定义了用作分隔符的字符集合

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

2)strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)

3)strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串 中的位置(会把这个标记换成\0)

4)strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。 

5)如果字符串中不存在更多的标记,则返回 NULL 指针。 

 strtok函数的使用

solitary@walk.at.123#99 对这个字符串进行提取

int main()
{char a1[] = "solitary@walk.at.123#999";char copy[50];//对a1临时拷贝strcpy(copy, a1);char sep[] = "#@.";//分隔符集合char* ret = strtok(copy, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);return 0;
}

 不知道各位有没有这样的问题:对于这个字符串的具体内容我是知道的,我直接进行这种暴力的打印就可以,那要是这个字符串很长很长,你是否又知道需要具体打印多少次呢?

这里我们借助循环就可以实现,只不过需要我们对strtok这个函数非常的了解

可能有的老铁就会说:我们不是一般借助循环来进行次数的判断嘛。这就说明了我们见的少了吧

当字符串不在存在标记时,这个函数返回NULL  :依据这个点我们作为循环的判断条件

int main()
{char a1[] = "solitary@walk.at.123#999";char copy[50];//对a1临时拷贝strcpy(copy, a1);char sep[] = "#@.";//分隔符集合char* ret = strtok(copy, sep);//借助循环来实现字段的打印for (ret; ret != NULL; ret = strtok(NULL, sep)){printf("%s\n", ret);}return 0;
}
五:错误信息报告
       strerror

strerror函数通常用于处理发生在系统调用库函数(记住不是自己编写 的函数)出现的错误,该函数会返回一个指向错误消息字符串的指针

当库函数调用失败的时候,strerror函数会把错误码记录到errno这个变量中

errno是C语言的一个全局变量

 

 

      perror

perror这个函数功能和strerror函数功能是一样的:都是打印错误码对应的错误信息

perror这个错误码也是从errno这个全局变量里面拿到的

 

六:字符分类函数

七:内存函数
     memcpy

memcpy的前2个参数都是 vod*类型:因为内存块里存放的数据你不知道是什么类型

第三个参数:拷贝源数据总的字节数

  memcp的模拟实现分析:

1)对数据进行一个字节一个字节的拷贝

2)注意:对void* 类型指针不能直接解引用或者是加1操作

3)对指针强转具有临时性

4)对于内存块重叠的拷贝是不可以的(严格意义来说),一般涉及内存块重叠交给memmove函数

#include<stdio.h>
#include<string.h>
void* my_memcpy(void* dst, const void* sor, size_t num)
{char* ret = dst;//对目标空间起始地址保留while (num--){//一个字节的拷贝*(char*) dst = *(char*)sor; //dst++, sor++;  // err 强转具有临时性//(char*)dst++;  // errdst = (char*)dst + 1;sor = (char*)sor + 1;}return ret;
}
int main()
{char a1[20] = "xxxxxxxxxxxxxxxx";char a2[] = "yyyyy";my_memcpy(a1, a2, 4);printf("%s\n",a1);return 0;
}
          memmove

memmove模拟实现的分析

1)拷贝:从前往后拷贝还是从后往前拷贝

   若是从后往前拷贝  7拷贝到5的位置, 6拷贝到4的位置  ,原来的5已经被覆盖掉

   从前往后拷贝:5拷贝到3 的位置, 6拷贝到4的位置,7拷贝到5 的位置,刚好解决了数据覆盖的问题

注意看,此时  dst 的起始位置 要 > sor的起始位置 ,若果要是从前往后拷贝会不会出现问题

从前往后拷贝:3拷贝到5 的位置,4拷贝到6 的位置,原来5 的已经被覆盖掉了

从后往前拷贝:5拷贝到7 的位置,4拷贝到6 的位置,3拷贝到5的位置刚刚好解决了数据覆盖的问题

此时无论从前往后还是从后往前拷贝都会出现数据覆盖问题

不知道聪明的你,是否已经注意到一个问题就是到底从前往后还是从后往前拷贝其实是取决于dst 与sor的起始相对位置

对于这个函数的模拟其实是有2种方法实现

方法1:

dst < sor  从前往后拷贝

dst >= sor && dst <= sor+num  从后往前拷贝

dst > sor+num  从前往后拷贝

方法2:

dst < sor  从前往后拷贝

dst >= sor   从后往前拷贝

2) 从前往后拷贝的逻辑(这个就和memcpy模拟实现一样的)

    while (num--) 
        {
            //从前往后拷贝  ,和memcpy模拟实现一样
            //一个字节一个字节拷贝
            *(char*)dst = *(char*)sor;
            dst = (char*)dst + 1;
            sor = (char*)sor + 1;
        }

3)从后往前拷贝的逻辑

这里我们需要考虑如何拿到5这个数据对应在最后一个字节

 

 注意:num代表要拷贝字节的总数目

我知道sor+num 是从sor起始位置 跳过num个字节,对于上面的图而言就是指向6 的起始位置,sor+num-1不就是指向原内存块的最后一个字节嘛

代码逻辑: 
//先找到源内存块的最后一个字节  跳过num-1个字节指向最后一个字节while (num -- )  //先用后减减 (num不为0 ; 进入下面的同时-1){*((char*)dst +num)= *((char*) sor+num); }

这里借助方法2来实现模拟


void* my_memmove(void* dst, const void* sor, size_t num)
{char* ret = dst;assert(dst && sor);// 从前往后还是从后往前取余 dst 与 sor 起始位置谁比较在前if (dst > sor) {//从后往前拷贝,避免数据覆盖//先找到源内存块的最后一个字节  跳过num-1个字节指向最后一个字节while (num -- )  //先用后减减 (num不为0 ; 进入下面的同时-1){*((char*)dst +num)= *((char*) sor+num); }}else{while (num--) {//从前往后拷贝  ,和memcpy模拟实现一样//一个字节一个字节拷贝*(char*)dst = *(char*)sor;dst = (char*)dst + 1;sor = (char*)sor + 1;}}return ret;
}
 科普一下:对于内存块数据重叠拷贝按理说memmove就可以实现,但是当我们在VS的编译器来测试的时候,也能实现对 内存块数据重叠拷贝  

注意并不是所有 的编译器在调用标准库的memcpy都能实现内存块数据重叠拷贝,具体情况还是取决于编译器

memcmp
memmcmp函数原型:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );

参数介绍:

1)ptr1 和ptr2  分别指向各自对应内存块的起始地址

2) num : ptr1 和ptr2 指向内存块 的前个num字节进行比较

注意啦,友友们:这里的参数num(要比较的字节数) 和函数strncmp参数的num含义可不同,后者代表:2个字符串要比较的字符个数

还是惯例,接下来我们进行memcmp的模拟实现

 memcmp函数模拟实现
int my_memcmp(const void* str1, const void* str2, size_t num)
{assert(str1 && str2);//注意memcmp比较的过程是:;一个字节一个字节的比较(是对内存里的数据)while (num--){/*if (*(char*)str1 > *(char*)str2)return 1;else if (*(char*)str1 < *(char*)str2)return -1;*/if ((*(char*)str1 == *(char*)str2)){str1 = (char*)str1 + 1;str2 = (char*)str2 + 1;}elsereturn *(char*)str1 - *(char*)str2;}return *(char*)str1 - *(char*)str2;
}
int main()
{/*int a1[] = { 1,8,3,4,5,6,7,8,9 };int a2[] = { 1,2,12 };*/char a1[] = "abcd";char a2[] = "abmnj";int ret = memcmp(a1, a2, 2); //注意这里memcmp的第三个参数num表示要比较的前num个字节数而不是个数printf("%d", ret);return 0;
}
memset

    memset函数原型:  void * memset ( void * ptr, int value, size_t num );

    ptr:起始地址

    value:要设置的值  

    num:要设置的字节数

1)这个函数对内存块的设置,所以说value我们多数传参为字符(在C语言里,字符的本质也是数值)

2)对memset 模拟实现的关键也是在于第二参数

 我们需要考虑如何将int 类型数据转换成char 类型数据???

对于这个问题我暂时有2 个解决方案(目前理论以及实践上都可以说的过去)

方法1:对第二个参数进行改变:char val

方法2:第二个参数依然是int 类型

把int 类型数据转换成char 类型数据:对val % 256

        *((char*)p) = val % 256;

 对应完整代码

 方法2代码:

方法1代码:

 


结语:

以上就是关于字符串以及内存块相关函数的share,相信到这个,各位老铁们的知识量已经大增了吧,这些函数在我们日常的模拟题中也会涉及到,若是到时候用上,那岂不是很香嘛。在此我也衷心希望各位铁子们能够收获满满。

那话不多,你懂的,咱接下来走起!

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

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

相关文章

springboot 整合 ElasticSearch 方法 (二)

依赖 在pom.xml文件中需要引入3个依赖, 三个都必须有并且三个依赖的版本要一致, 不然会报错. 不一定是 7.6.1 这个版本 , 只需要保证这三个依赖的版本一致就可以了. <dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch<…

【C++入门到精通】特殊类的设计 |只能在堆 ( 栈 ) 上创建对象的类 |禁止拷贝和继承的类 [ C++入门 ]

阅读导航 引言一、特殊类 --- 不能被拷贝的类1. C98方式&#xff1a;2. C11方式&#xff1a; 二、特殊类 --- 只能在堆上创建对象的类三、特殊类 --- 只能在栈上创建对象的类四、特殊类 --- 不能被继承的类1. C98方式2. C11方法 总结温馨提示 引言 在面向对象编程中&#xff0…

Django框架(一)安装与创建项目

认识 Django Django是一个高级Python Web框架&#xff0c;它鼓励快速开发和简洁、实用的设计。它由经验丰富的开发人员构建&#xff0c;解决了Web开发的大部分麻烦&#xff0c;因此您可以专注于编写应用程序&#xff0c;而无需重新发明轮子。它是免费和开源的。 Django官网地…

Java笔记 --- 五、File

五、File 概述 将字符串变成File对象&#xff0c;再去使用里面的方法 父级路径&#xff1a;除了文件本身的路径 C:\Users\Desktop 子级路径&#xff1a;文件名 m.txt 常见的成员方法 判断、返回 length 只能获取文件的大小(字节数量) 创建、删除 delete方法默认只能删除…

行测-资料:1. 速算技巧、基期与现期

1、速算技巧 1.1 截位直除 1.1.1 截位 1.1.2 截谁 1.1.3 截几位 选项差距大&#xff1a; 四个选项首位均不同首位相同&#xff0c;第二位差大于首位 选项差距小&#xff1a; 首位相同且第二位差小于等于首位 例子 C&#xff0c;截两位。 C&#xff0c;截两位。 B&#xff0c;截…

JS中的try...catch

一、定义和结构 作用&#xff1a;捕获同步执行代码下的异常错误 在没有使用try...catch的情况下&#xff0c;同步代码执行遇到异常会报错&#xff0c;并中断后续代码执行&#xff1b; 在使用try...catch的情况下&#xff0c;同步代码执行遇到异常会抛出异常&#xff0c;并继续…

[Linux]HTTP状态响应码和示例

1xx&#xff1a;信息响应类&#xff0c;表示接收到请求并且继续处理 2xx&#xff1a;处理成功响应类&#xff0c;表示动作被成功接收、理解和接受 3xx&#xff1a;重定向响应类&#xff0c;为了完成指定的动作&#xff0c;必须接受进一步处理 4xx&#xff1a;客户端错误&#x…

基于ncurse的floppy_bird小游戏

1. 需求分析 将运动分解为鸟的垂直运动和杆的左右运动。 2. 概要设计 2.1 鸟运动部分 2.2 杆的运动 3. 代码实现 #include <stdio.h> #include <ncurses.h>#include <stdlib.h> #include <time.h>int vx 0; int vy 1;int bird_r; int bird_c;int…

奇怪问题说 - 测试篇

文章目录 1.什么是软件测试2.软件测试和开发的区别3.软件测试的发展&#xff1a;4.软件测试岗位5.软件测试在不同类型公司的定位6.一个优秀的软件测试人员具备的素质6.1综合能力6.2掌握自动化测试技术6.3优秀的测试用例设计能力6.4探索性思维6.5有责任感和一定的压力 7.软件测试…

物联网IOT: 风浆叶片拧紧装配及实时监测系统

某大型风电设备,通过机器人应用与精益化生产体系的融合,打造出行业领先的具备柔性生产能力的“脉动式”生产体系。同时在关键工序上。其中,在叶片装配等关键工序上使用由智能机器人代替人工,以提高生产的效率和装配质量可靠性,将六轴机器人、视觉系统、光电系统、液压、气动、伺…

AMEYA360--思瑞浦推出16通道高精度ADC—TPAFE51760

聚焦高性能模拟芯片和嵌入式处理器研发的半导体公司——思瑞浦推出全新16通道高精度ADC——TPAFE51760。 TPAFE51760内置高精度基准&#xff0c;工作温度支持-40C to 125C&#xff0c;产品广泛应用于电力自动化领域中的DTU、FTU、MU等装置。 TPAFE51760产品优势 业界领先的30V模…

2013年苏州大学837复试机试C/C++

2013年苏州大学复试机试 第一题 题目 假设有一堆数字&#xff08;小于100个&#xff09;需要对其做如下处理&#xff1a; 求平均数求标准差求方差 可用函数实现也可以不用 代码 #include <iostream> #include <sstream> //字符串流 #include <cmath> …

拦截器的简单使用

拦截器的简单使用 拦截器的使用创建拦截器preHandle 目标方法执行前执行postHandle 目标方法执行后执行afterCompletion 视图渲染后执行 拦截器使用场景返回值注册拦截器运用拦截器 拦截器的使用 创建拦截器 首先,我们需要创建一个拦截器器的类,并且需要继承自HandlerIntercep…

Linux服务器配置与管理(第二次实验)

实验目的及具体要求 目的 1.掌握基于命令行的文件操作 2.掌握基于命令行的目录操作 3.掌握用户账户的命令行操作 4.掌握组账户的命令行操作 5.熟悉磁盘分区操作 6.掌握调整优先级的方法 具体要求 1.掌握基于命令行的文件和目录操作 ①创建测试目录 ②创建文件 ③复…

细数语音识别中的几个former

随着Transformer在人工智能领域掀起了一轮技术革命&#xff0c;越来越多的领域开始使用基于Transformer的网络结构。目前在语音识别领域中&#xff0c;Tranformer已经取代了传统ASR建模方式。近几年关于ASR的研究工作很多都是基于Transformer的改进&#xff0c;本文将介绍其中应…

python 基础知识点(蓝桥杯python科目个人复习计划25)

今日复习内容&#xff1a;基础算法中的进制转换 1.任意进制转十进制 &#xff08;1&#xff09; 基数&#xff1a;表示奇数数字符号的个数 10进制&#xff1a;0--9&#xff0c;基数为1016进制&#xff1a;0--9&#xff0c;A--F&#xff0c;基数为16 &#xff08;2&#xff…

计算机找不到ucrtbased.dll无法运行程序,分享5种有效的解决方法

当计算机系统在运行过程中无法找到ucrtbased.dll这个特定的动态链接库文件时&#xff0c;可能会引发一系列的问题和故障现象。ucrtbased.dll是Windows操作系统中一个至关重要的组件&#xff0c;它包含了C运行时库的核心函数&#xff0c;对于许多应用程序特别是基于Microsoft Vi…

【论文+App试玩+图像到视频】2311.Animate-anyone:上传1张图片为任何人制作动画(用于角色动画的一致且可控的图像到视频合成)(暂未开源)

项目主页&#xff1a;https://humanaigc.github.io/animate-anyone/ 论文: Animate Anyone: Consistent and Controllable Image-to-Video Synthesis for Character Animation 摩尔线程复现代码&#xff1a;https://github.com/MooreThreads/Moore-AnimateAnyone 摩尔windows一…

第9章 多线程

第9章 多线程 学习目标 了解进程和线程的区别 能够理解并发与并行的区别 能够使用继承类的方式创建多线程 能够使用实现接口的方式创建多线程 能够说出实现接口方式的好处 能够解释安全问题的出现的原因 能够使用同步代码块解决线程安全问题 能够使用同步方法解决线程安全问题…

多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测

多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测 目录 多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测效果一览基本介绍程序设计参考资料 效…