当前位置: 首页 > news >正文

strcpy和strncpy和strcat和strncat和strstr和strtok函数使用及实现

目录

strcpy和strncpy函数的原理及实现

strcpy函数

my_strcpy优化代码 

函数返回值的疑惑

strncpy函数

strncpy自定义

完整代码

字符串追加函数 strcat和strncat

strcat函数

讲解和使用

过程解析

strcat代码

strcat无法给自己追加

strncat函数

讲解和使用

 过程解析

strncat代码

strncat可以给自己追加

使用并实现strstr与strtok:查找与切割

strstr函数

strstr函数介绍

 自定义实现strstr

strtok函数

strtok函数介绍

strtok参数讲解

 strtok使用时的注意事项

 for循环在strtok使用中的妙处

自定义实现strtok


strcpy和strncpy函数的原理及实现

strcpy函数

strcpy(str1,str2),函数作用是将 把含有'\0'结束符的字符串,比如这里是将str2拷贝到str1中去('\0'也同时拷贝进去),strcpy返回值的类型为char* 

在传参的时候传递的是字符串首地址,如:char* p=“hello”是将h的地址传给了p

1、在使用strcpy函数时,要保证目标处有足够的空间存放原字符串(否则无法拷贝进去):

 2、目标空间必须可修改(比如数组),否则无法打印:

自定义实现的错误1:未将'\0'拷贝进去

void fail_str(char* arr1, char* arr2)
{while (*arr2 != '\0'){*arr1 = *arr2;arr1++;arr2++;}
}

错因在于:当 *arr2为 ‘\0’,没有进入循环,没有将\0拷贝入arr1,所以无法覆盖后面多余的****

正确代码如下:>

#include <stdio.h>void my_strcpy(char* arr1, const char* arr2)
{while (*arr2 != '\0'){*arr1 = *arr2;arr1++;arr2++;}*arr1 = *arr2;
}int main()
{char str1[] = "***************";char str2[] = "hello world";my_strcpy(str1, str2);printf("%s\n", str1);return 0;
}

代码优化如下:

观察while循环,发现代码可以继续优化:>

当*arr2为'\0'时终止循环,此时已经完成了条件的执行:将\0赋给*arr1

在whiel循环中条件表达式比循环体要多执行一次。


void my_adv_strcpy(char* arr1, const char* arr2)
{while (*arr1++ = *arr2++);
}

由于strcpy返回值类型为char*,故最终完整代码如下:>

my_strcpy优化代码 

#include <stdio.h>
char* my_strcpy(char* str1, const char* str2)
{char* ret = str1;while (*str1++ = *str2++);return ret;
}
int main()
{char str1[] = "***************";char str2[] = "hello world";my_strcpy(str1, str2);printf("%s\n", str1);return 0;
}

函数返回值的疑惑

有人会问:“strcpy只是拷贝字符串,为什么还需要返回值?”

 如果用void来实现可以吗? 答案是可以的。

 返回值为char*的目的是让它能够进行链式访问

比如你想计算复制后的字符串长度,如下:>

int L = strlen(strcpy(str1, str2));


strncpy函数

 strncpy和strcpy的区别:>

strcpy() 函数用来复制字符串

而strncpy()只用来复制字符串的前n个字符,所以需要再传递一个参数n

strcpy会向dest中追加src的结束标记'\0'

strncpy()不会向dest追加src的结束标记'\0'

注意:当传递个数大于所传递的字符串长度时,后面传递的都\0

 图示1,n小于字符串长度

 图示2,n大于字符串长度

strncpy自定义

char* my_strncpy(char* dest, const char* src, int n)
{char* ret = dest;//将dest首地址存入ret中while(n)//循环n次{*dest=*src;dest++;src++;n--;}return ret;//将dest首地址返回
}

完整代码

char* my_strncpy(char* dest, const char* src, int n)
{char* ret = dest;while(n--){*dest++ = *src++;}return ret;
}
int main()
{char a[30] = "abcdddddddd";char b[] = "abcde";printf("%s", strncpy(a, b, 7));
}

字符串追加函数 strcat和strncat

先下结论:strncat比strcat更安全、方便、好用!

strcat函数

讲解和使用

strcat实现字符串的追加

头文件:<string.h>

注意:

1、两个字符串都要以'\0'结束

2、目标空间可修改

3、要保证destination空间足够追加源字符

4、追加之后source不变,destination发生改变

函数用法如下图:>

过程解析

1、返回值为   char*   类型

2、source会覆盖destination结尾的'\0'

3、source的'\0'会被加上去

char* my_strcat(char* str1, const char* str2)
{}

过程的实现:>

简化如下:> 

while的条件判断比循环要多一次,所以最后*src为\0时,条件判断中的赋值会进行一次

之后由于条件为0(假),就跳出while

strcat代码

char* my_strcat(char* dest, const char* src)
{char* ret = dest;while (*dest++);dest--;while (*dest++ = *src++);return ret;
}
int main()
{char arr1[30] = "hello ";char arr2[] = "world";printf("追加前arr1:>%s\n", arr1);printf("追加前arr2:>%s\n", arr2);my_strcat(arr1, arr2);printf("追加后arr1:>%s\n", arr1);printf("追加后arr2:>%s\n", arr2);
}

strcat无法给自己追加

由于strcat会从\0开始覆盖,如果写成strcat(arr,arr),会造成死循环,没有尽头


strncat函数

讲解和使用

 头文件:<string.h>

辨析两者:

1、strcat 函数的结束条件是src 来到了 '\0';

     strncat 函数结束的条件则是所要追加的字符个数为0;

2、strcat函数把源字符串整个地连在目标字符串后面;

      strncat可以控制连接的字符个数,用n表示

3、前面讲到strcat无法连接自身。但是strncat可以做到,同时可以控制连接字符的个数。

4、如果n要大于源字符串的长度,则未追加完的字符补充为\0


 过程解析

首先确定返回类型和函数参数

char* my_strncat(char* dest, char* src, int n)

类似strcat,先找到\0的位置,进行覆盖追加,直到将n个字符全部加完

char* my_strncat(char* dest, char* src, int n)
{char* ret = dest;while (*dest++);dest--;//找到\0while (n--){*dest++ = *src++;//当src到达\0时,将\0给dest并且返回dest首地址if (*src == '\0'){*dest = *src;return ret;}}//全部追加完之后补充\0*dest = '\0';return ret;
}

简化之后如下:>

char* my_strncat(char* dest, char* src, int n)
{char* ret = dest;while (*dest++) ;dest--;while (n--){//条件执行之后再判断,若此时字符串来到为\0//0==0,条件判断为真,返回retif ((*dest++ = *src++) == 0)return ret;}*dest = '\0';return ret;
}

strncat代码

char* my_strncat(char* dest, char* src, int n)
{char* ret = dest;while (*dest++) ;dest--;while (n--){if ((*dest++ = *src++) == 0)return ret;}*dest = '\0';return ret;
}
int main()
{char arr1[30] = "hello ";char arr2[] = "world";printf("追加前arr1:>%s\n", arr1);printf("追加前arr2:>%s\n", arr2);my_strncat(arr1, arr2,3);printf("追加后arr1:>%s\n", arr1);printf("追加后arr2:>%s\n", arr2);
}

strncat可以给自己追加

使用并实现strstr与strtok:查找与切割

 目录

strstr函数

strstr函数介绍

自定义实现strstr

strtok函数

strtok函数介绍

strtok参数讲解

strtok使用时的注意事项

for循环在strtok使用中的妙处

自定义实现strtok


strstr函数

strstr函数介绍

strstr(str1,str2)

作用:查找指定字符串

在字符串str1中查找是否含有字符串str2

如果有,返回str2在str1中第一次出现的地址

否则返回空指针NULL

注意:如果str2是一个空字符串,则返回str1

 函数声明:char * strstr (const char * str1, const char * str2)

由于只是进行查找而不改变原来的两个字符串,所以使用const修饰会更加安全

函数使用图示:

当找到第一个完全相同字符串后,返回该位置字符串的地址(并不会截断)

 自定义实现strstr

核心:

能找到:>

1、将str1首地址放入空瓶flag中

2、在str1中遍历,直到找到和str2首字符相同的字符,将此地址存入flag

3、找到第一个字符相同之后,str1和str2一起后移,依次判断后面的字符是否都相同

4、如果相同,则说明找到了,将flag中保存的地址进行return

如果找不到呢?

  在每一次经过上述的3 步骤之后,让flag往后移。

  直到碰到str1中的\0,说明str1遍历完都没找到与str2相同的字符串

  这时候返回NULL

#include <stdio.h>
char* my_strstr( const char* str1,  const char* str2)
{//断言是否为空指针assert(str1 && str2);const char*s1 = NULL;const char*s2 = NULL;//特殊情况(str2为空字符串)if(str2=='\0')return str1;//用flag记录str1中与str2相同字符的地址const char* flag = str1;//如果遍历str1都没有找到//直到flag为\0时,跳出whilewhile (*flag){s1 = flag;s2 = str2;//寻找相同字符while(*s1&&(*s1 == *s2)){s1++;s2++;}//如果最终遍历s2中来到\0//说明找到了与s2中相同的字符串if (*s2 == '\0')return (char*)flag;//此时将flag地址返回flag++;}return NULL;//找不到就返回空指针
}
int main()
{char arr1[] = "abcdefgabc";char arr2[] = "defg";printf("%s", my_strstr(arr1, arr2));
}

strtok函数

strtok函数介绍

头文件<string.h>

strtok(str,sep)

str表示字符串,sep就是Seperator意思是分割符,这里用来表示多个分割符的集合

作用:分割字符串,一次切割一段

示例如下:>


strtok参数讲解

char *strtok ( char* str , const char * delimiters )

第一个参数是需要切割的字符串。
该字符串中可以包含 0个、1个、多个分割符

第二个参数就是我们指定的切割符集合。

假设想要分别得到Hello,world,!

那么分隔符中就只需要有空格

如果要将以下字符串切割,那么分隔符中需要包含,;。这三个字符

 strtok使用时的注意事项

char *strtok ( char* str , const char * delimiters )

1、strtok函数找到str中的一个分割符之后,会将其用 \0 覆盖并且作为结尾,之后再返回一个指向这个\0的指针。

注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容。

char *strtok ( char* str , const char * delimiters )

2、第一次切割时,第一个参数(也就是str)不能是NULL

      第一次切割时,函数将找到str中第一个分割符,将它替换为\0

      同时strtok函数将保存这 个\0在整个字符串中的位置。

 char *strtok ( char* str , const char * delimiters )

3、第二次、第三次...第n次使用strtok函数切割同一个字符串时。

      第一个参数为NULL

      此时函数将从上一个保存的\0的位置开始往后切割,直到遇到下一个分割符或\0

 for循环在strtok使用中的妙处

for(初始化;循环条件的控制;循环条件的改变)
{}

for循环中的初始化只执行一次。

而strtok函数在第一次使用时传递的第一个参数为字符串

之后传递的第一个参数都是NULL

利用for循环这一特性可以更好地使用strtok函数

int main()
{char arr[] = "C语言,Java;C++。hello world";char flag[] = ",;。 ";char* tmp = arr;char* ret = NULL;for (ret = strtok(tmp, flag); ret != NULL; ret = strtok(NULL, flag)){puts(ret);}
}

自定义实现strtok

核心:

第一次使用:

第一个参数为字符串。首先记录字符串首地址,接着将字符串挨个与分割符集合中的每个分割符进行比较,在字符串中找到了一个分割符之后就停止,返回字符串首地址,同时将该分割符置为\0,并且记录这个\0的地址。

第二次使用:

第一个参数为NULL。首先,将上一次查找之后的\0地址传递过来,从该\0的下一个位置开始,记为第二个字符串的首地址,再次遍历分割符之后找到某个分割符。将该分割符置为\0同时记录地址。

...

第N次使用同理。

(重点:函数需要能够记录上一次调用后保存的\0的位置,所以我们使用static用于记录)

代码如下:>

#include <stdio.h>
#include <string.h>char* my_strtok(char* str1, const char* str2)
{static char* flag = NULL;//标记每一个分割符的位置static char* start = NULL;//每一次切割的字符串的首地址static int length = 0;static int count_str1 = NULL;//记下目前遍历过的字符总长度,与str1的长度进行比较int sz_delm = strlen(str2);//分隔符数量,循环遍历所有分隔符
//1、第一次进入时if (str1 != NULL){length = strlen(str1);//保存整个str1的长度start = str1;//str1的首地址存入for (*str1; *str1 != '\0'; str1++){for (int i = 0; i < sz_delm; i++){if (i == 0) count_str1++;//每次查找时给count加1if (*str1 == *(str2 + i)){*str1 = '\0';//将分割符置为\0flag = str1;//将该分割符的地址保存下来return start;}}}}
//2、之后的每一次进入函数时,第一个参数都是空指针else{start = flag + 1;str1 = start;//这里的for循环和第一次一样。for (*str1; *str1 != '\0'; str1++){for (int i = 0; i < sz_delm; i++){if (i == 0) count_str1++;if (*str1 == *(str2 + i)){*str1 = '\0';//将分割符置为\0flag = str1;//记录这一次置0的位置。return start;}}}
//3、找不到分割符就返回NULLif (count_str1 > length)return NULL;//保证每个控路都有返回值return start;}
}int main()
{char arr[50] = "C语言,Java;C++?OK";char* delm = ",;?";char* tmp = NULL;for (tmp = my_strtok(arr, delm); tmp != NULL; tmp = my_strtok(NULL, delm)){puts(tmp);}
}


http://www.mrgr.cn/news/96533.html

相关文章:

  • MIPS-32架构(寄存器堆,指令系统,运算器)
  • 第三次作业
  • Epub转PDF软件Calibre电子书管理软件
  • LLM实践(二)——基于llama-factory的模型微调
  • mybatis里in关键字拼接id问题
  • GOF23种设计模式
  • 从Web到桌面:深入解析Electron的技术架构与应用实践
  • RK3588,V4l2 读取Gmsl相机, Rga yuv422转换rgb (dma), 实现零拷贝
  • Docker实现MySQL主从复制配置【简易版】
  • AutoDIR: Automatic All-in-One Image Restoration with Latent Diffusion 论文阅读 ECCV
  • UE5 学习笔记 FPS游戏制作30 显示击杀信息 水平框 UI模板(预制体)
  • .js项目编译成.exe程序(交叉编译全过程整理)
  • Docker使用ubuntu
  • 浅析车规芯片软错误防护加固的重要性
  • 设计模式之适配器模式(二):STL适配器
  • 房贷计算器
  • 验证码通过“Canvas 绘制”与“Base64 图片”渲染两种不同的实现方式显示
  • C++ 继承:面向对象编程的核心概念(二)
  • Dust3r、Mast3r、Fast3r
  • docker-compose部署prometheus+grafana+node_exporter+alertmanager规则+邮件告警