贝蒂详解<string.h>哦~(用法与实现)

目录

引言:

(一)字符函数和字符串函数

 1.简介

 2.strlen()函数

      2.1用法

      2.2实例

       2.3 实现strlen()

       (1)计数法

       (2)递归法

    (3) 指针-指针

   2.4sizeof和strlen()的区别

3.strcmp()函数

  3.1用法

3.2实例

    3.3实现strcmp()

 4.strcpy()函数

     4.1用法

     4.2实例

     4.3 实现strcpy()

5.strcat()函数

       5.1用法

       5.2实例

      5.3实现strcat() 

6.strchr()函数 

    6.1用法

    6.2实例 

6.3实现strchr()

 7.strstr()函数

      7.1用法

       7.2实例

    7.3 实现strstr()

  8.strncmp()函数

         8.1用法

         8.2实例 

         8.3 实现strncmp()

  9.strncpy()函数

     9.1用法

      9.2 实例

     9.3实现strncpy()函数 

  10.strncat()函数 

       10.1用法

       10.2 实例

       10.3实现strncat()函数

  11.strtok()函数 

    11.1用法

    11.2 实例

   11.3 实现strtok()函数

12.strerror()函数

    12.1用法

   12.2实例

(二)内存操作函数

 1. 简介

 2. memset()函数

   2.1用法

2.2实例

2.3 实现memset() 

3.  memcmp()函数

3.1用法

 3.2 实例

3.3 实现memcmp() 

3.4strcmp,strncmp,memcmp之间的区别 

4. memcpy()函数 

4.1用法

 4.2 实例

4.3实现memcpy() 

4.4strcpy,strncpy,memcpy之间的区别

5. memmove()函数

5.1用法

 5.2实例

5.3实现memmove()

结言:


引言:

        我们在学习C语言的过程中,除了使用最多的头文件<stdio.h>,还会使用其他头文件,利用其中的库函数帮助我们简化代码的过程,比如像<math.h>,<string.h>等头文件,而今天贝蒂就带大家详细了解一下<string.h>。

(一)字符函数和字符串函数

 1.简介

        <string.h>中有很多实用的库函数,大致分为两类:一类是像strlen(),strchr()等作用于字符或字符串的字符函数和字符串函数,今天就让我们先来介绍字符函数和字符串函数吧~

 2.strlen()函数

      2.1用法

1.声明:size_t strlen(const char *str)

  • str -- 要计算长度的字符串。

2.作用:计算字符串 str 的长度,直到空结束字符('\0'),但不包括空结束字符。

3.返回值:该函数返回字符串的长度

      2.2实例

        strlen()函数的用法很简单,贝蒂来简单介绍一下吧~

#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "abcdef";int len = strlen(arr);//计算arr字符串的长度printf("%d\n", len);return 0;
}

 输出结果: 6

       2.3 实现strlen()

        我们已经知道了strlen()函数的用法,那我们可不可以自我实现一个my_strlen()函数来模拟strlen()函数的功能,当然是可以的呀,下面贝蒂将介绍三种实现方法。

       (1)计数法

思路:我们可以用一个指针变量p指向首元素和一个计数变量count并初始化为0,然后循环解引用指针所指向的元素,判断这个元素是否为‘\0’,不是每次p++,count++,是就跳出循环,返回count。

       代码实现如下:

#include<stdio.h>
int my_strlen(char* p)
{int  count = 0;while (*p)//当指向'\0',也就是0,为假跳出循环{p++;//指向下一个元素count++;//计数}return count;
}
int main()
{char arr[] = "abcdef";int len = my_strlen(arr);//计算arr字符串的长度printf("%d\n", len);return 0;
}
       (2)递归法

 思路:假设我们要计算字符串“abcdef”的长度,我们可以拆分为1+“bcdef”的长度,同理“bcdef”的长度可以拆分为1+“cdef”的长度......理解了这个思路,我们就可以实现递归,首先定义一个指针变量p,如果*p!='\0',我们就把p+1作为参数调用本函数,直到*p为0.

         代码实现如下:

int my_strlen(char* p)
{if (*p != '\0'){return 1 + my_strlen(p + 1);//每次调用p+1指向下一个元素}else{return 0;//结束递归}
}
int main()
{char arr[] = "abcdef";int len = my_strlen(arr);//计算arr字符串的长度printf("%d\n", len);return 0;
    (3) 指针-指针

        首先大家要清楚指针-指针是指在同一空间内,两个指针之间的元素个数。

思路:首先定义两个指针p1,p2,让两个指针指向首元素,然后让一个指针p2循环++,直到指向‘\0’就停止,最后返回p2-p1。

        代码实现如下:

int my_strlen(char* p1)
{char* p2 = p1;//使两个指针都指向首元素while (*p2){p2++;}return p2 - p1;//返回两指针直接的元素的个数就是其长度
}
int main()
{char arr[] = "abcdef";int len = my_strlen(arr);//计算arr字符串的长度printf("%d\n", len);return 0;
}

   2.4sizeof和strlen()的区别

sizeof() 和 strlen() 的主要区别在于:

  • sizeof() 是一个运算符,而 strlen() 是一个函数。
  • sizeof() 计算的是变量或类型所占用的内存字节数,而 strlen() 计算的是字符串中字符的个数。
  • sizeof() 可以用于任何类型的数据,而 strlen() 只能用于以空字符 '\0' 结尾的字符串。
  • sizeof() 计算字符串的长度,包含末尾的 '\0',strlen() 计算字符串的长度,不包含字符串末尾的 '\0'。

       贝蒂说:“sizeof和 strlen() 分别是 C 语言中两个非常常用的关键字和函数,它们都与计算内存大小有关,但是它们的作用是不同的哦,大家一定要区分清楚哦~。 

3.strcmp()函数

  3.1用法

1.声明:int strcmp(const char* str1,const char*str2)

  • str1 -- 要进行比较的第一个字符串。
  • str2 -- 要进行比较的第二个字符串。

2.作用:strcmp() 会根据 ASCII 编码依次比较 str1 和 str2 的每一个字符,直到出现不到的字符,或者到达字符串末尾(遇见‘\0’

3.返回值:

  • 如果返回值小于 0,则表示 str1 小于 str2。
  • 如果返回值大于 0,则表示 str1 大于 str2。
  • 如果返回值等于 0,则表示 str1 等于 str2。

3.2实例

   strcmp用于比较字符串,并返回>0,==0,<0的值,让我们看看他的具体使用吧

#include<stdio.h>
#include<string.h>
int main()
{char str1[] = "abcd";char str2[] = "Abcd";char str3[] = "abcd";char str4[] = "bbcd";int ret1 = strcmp(str1, str2);//比较str1与str2int ret2 = strcmp(str1, str3);//比较str1与str3int ret3 = strcmp(str1, str4);//比较str1与str4printf("%d %d %d\n", ret1, ret2, ret3);return 0;	
}

输出:1 0 -1

 贝蒂说:“strcmp()首先会比较第一个字母的ASCII值,如果==则比较第二个字符,直到遇见'\0',若不相等,则返回两个字符之差”  

    3.3实现strcmp()

   思路:首先两个字符串不能改变,且不能传的参数不能为空指针,输入空指针让编译器报错,然后从第一个字符开始比较,直到两个字符不相等(返回两个字符之差),如果在不相等之前已经指向‘\0’,直接返回0.

     代码实现如下:

#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
//经const修饰让*str1与*str2无法改变
{assert(str1 && str2);//判断str1和str2是否为空指针//空指针直接报错,头文件<assert.h>while (*str1== *str2){if (*str1 == '\0'){return 0;}str1++;str2++;}return *str1 - *str2;
}
int main()
{char str1[] = "abcd";char str2[] = "Abcd";int ret = my_strcmp(str1, str2);printf("%d\n", ret);return 0;
}

       贝蒂说:“相信大家也和贝蒂一样疑惑过,为什么比较字符串不能用==,而是用库函数相比较,就让贝蒂告诉你吧,嘻嘻,那是因为字符串在内存中存储是以首元素地址方式存储的,比较两个地址,肯定会不相等呀~” 

 4.strcpy()函数

     4.1用法

1. 声明:char *strcpy(char *dest, const char *src),dest -- 指向用于存储复制内容的目标数组,src -- 要复制的字符串。

2. 作用:把 src 所指向的字符串复制到 dest。需要注意的是如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。

3. 返回值:该函数返回一个指向最终的目标字符串 dest 的指针。

     4.2实例

#include <stdio.h>
#include <string.h>
int main()
{char src[40];char dest[40];strcpy(src, "im not betty");strcpy(dest, src);printf("最终的目标字符串:%s\n", dest);return 0;
}

输出结果: 最终的目标字符串:im not betty 

     4.3 实现strcpy()

 思路:我们想要将src的内容拷贝进des中,首先src的内容不能被改变,且保证都不是空指针,然后循环将src的内容赋值给des,直到赋值完‘\0’,条件为假,跳出循环。

代码实现: 

#include<assert.h>
char* my_strcpy(char* des, const char* src)//防止src的内容被改变
{assert(des && src);//防止des与src是空指针char* ret = des;//作为返回值while (*des++ = *src++)//循环拷贝,当拷贝完'\0',判断为假,跳出循环{;}return ret;
}
int main()
{char src[40] = "abcdef";char des[40];my_strcpy(des, src);//将src的内容拷贝去desprintf("%s\n", des);return 0;
}

      贝蒂说:“strcpy(),cpy,copy,就是把别人的东西copy(复制)下来呀~,嘻嘻” 

5.strcat()函数

       5.1用法

1. 声明:char *strcat(char *dest, const char *src)

  • dest -- 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串。
  • src -- 指向要追加的字符串,该字符串不会覆盖目标字符串。

2. 作用:把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。

3.返回值:该函数返回一个指向最终的目标字符串 dest 的指针。

       5.2实例

#include <stdio.h>
#include <string.h>
int main()
{char src[50];char dest[50];strcpy(src, "world!");strcpy(dest, "hello ");strcat(dest, src);printf("最终的目标字符串:%s", dest);return 0;
}

 输出结果:最终的目标字符串:hello world!

贝蒂说:“因为strcat()函数的实现机制,所以strcat()无法对自己追加(会出现死循环),如果要实现自己对自己的追加可以使用strncat()函数,这个后面贝蒂会为大家讲解哒~”  

      5.3实现strcat() 

思路:实现strcat的方法其实和实现strcpy的方法类似,都是替换,只是要让dest先指向末尾'\0'。

 代码实现:

char* my_strcat(char* dest, const char* src)//防止src的值被改变
{assert(dest && src);//不能为空指针char* ret = dest;while (*dest)//使dest指向末尾{dest++;}while (*dest++ = *src++)//循环赋值{;}return ret;
}
int main()
{char dest[20] = "hello ";char src[20] = "world!";my_strcat(dest, src);printf("%s", dest);return 0;
}

6.strchr()函数 

    6.1用法

1. 声明:char *strchr(const char *str, int c)

  • str -- 要查找的字符串。
  • c -- 要查找的字符。

2. 作用:在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。

3. 返回值:如果在字符串 str 中找到字符 c,则函数返回指向该字符的指针,如果未找到该字符则返回 NULL。

    6.2实例 

#include<string.h>
int main()
{char arr[20] = "hello betty";char* p = strchr(arr,'y');if (*p == NULL){printf("没找到\n");}else{printf("找到了\n");}return 0;
}

6.3实现strchr()

思路:一样先排查空指针,然后循环寻找,如果寻找到,直接返回其地址。找不到就返回空指针NULL

代码实现: 

char* my_strchr(const char* str, int c)
{assert(str);//排查空指针while (*str){if (*str == c){return str;//找到返回其地址}str++;}return NULL;//找不到返回空指针
}
int main()
{char arr[20] = "hello betty";char* p =my_strchr(arr,'y');if (*p == NULL){printf("没找到\n");}else{printf("找到了\n");}return 0;
}

        贝蒂说:“strchr()函数在我们做有关字符串的问题时候非常常用,大家可以重点掌握哦~” 

 7.strstr()函数

      7.1用法

 1. 声明:char *strstr(const char *haystack, const char *needle)

      haystack -- 要被检索的 C 字符串。

      needle -- 在 haystack 字符串内要搜索的小字符串。

 2 .作用:在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 '\0'。

 3 .返回值:该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 空指针(NULL)

       7.2实例

#include <stdio.h>
#include <string.h>
int main()
{char haystack[20] = "hello betty";char needle[10] = "betty";char* ret = strstr(haystack, needle);if (ret == NULL){printf("未找到\n");}else{printf("找到啦,子字符串是:%s\n", ret);}return 0;
}

输出结果:找到啦,子字符串是:betty

    7.3 实现strstr()

思路:首先用是用s1,s2指向两个字符串的首元素,用p记录str1中开始比较的元素的位置,方便重新开始比较。然后循环比较,如果*s1!=*s2,或者遇见‘\0’,就跳出循环,判断,如果是s2为‘\0’,说明配对成功,s1为‘\0’,则说明后续长度不够,匹配失败啦,除开以上情况,就让p++,重复上述流程,直到*p==‘\0’

 情况1:在acbcef中查找acb

   

代码实现如下:

#include <stdio.h>
char* my_strstr(const char* str1, const char* str2)
{assert(str1 && str2);//防止空指针const char* s1 = str1;const char* s2 = str2;const char* p = str1;//记录初始位置while (*p){s1 = p;//从记录位置开始比较s2 =str2;while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2){s1++;s2++;}if (*s2 == '\0')//配对成功{return (char*)p;//p原本被const修饰}else if (*s1 == '\0' && s2 != '\0')//s1后续字符少于s2{return NULL;}p++;//记录下一个位置}return NULL;
}
int main()
{char haystack[20] = "hello betty";char needle[10] = "betty";char* ret =my_strstr(haystack, needle);if (ret == NULL){printf("未找到\n");}else{printf("找到啦,子字符串是:%s\n", ret);}return 0;
}

          贝蒂说:“整体来说strstr的实现方法比较复杂,需要大家细细揣摩,最好画图慢慢分析哦~” 

  8.strncmp()函数

         8.1用法

1. 声明:int strncmp(const char *str1, const char *str2, size_t n)

  • str1 -- 要进行比较的第一个字符串。
  • str2 -- 要进行比较的第二个字符串。
  • n -- 要比较的最大字符数。

2. 作用: 把 str1 和 str2 进行比较,最多比较前 n 个字符

3. 返回值:

  • 如果返回值 < 0,则表示 str1 小于 str2。
  • 如果返回值 > 0,则表示 str1 大于 str2。
  • 如果返回值 = 0,则表示 str1 等于 str2。

         8.2实例 

#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "abcdef";char arr2[20] = "abcddd";int ret1 = strncmp(arr1, arr2, 3);//比较前三个字符int ret2 = strncmp(arr1, arr2, 8);//即使给出的num太大,遇见'\0'也会停止int ret3 = strncmp(arr1, arr2, 6);//比较全部字符printf("%d %d %d\n", ret1, ret2,ret3);return 0;
}

输出结果:0 1 1 

         8.3 实现strncmp()

思路:总体思路与strcmp()的设计思路一样,只需加入限制比较字符个数的条件即可~

 代码实现: 

#include <stdio.h>
#include<assert.h>
int my_strncmp(const char* str1, const char* str2, size_t n)
{assert(str1 && str2);while (n-- && *str1 == *str2)//任意一个条件不满足就跳出循环{if (*str1 =='\0'){return 0;}str1++;str2++;}return *str1 - *str2;
}

  9.strncpy()函数

     9.1用法

1. 声明:char *strncpy(char *dest, const char *src, size_t n)

  • dest -- 指向用于存储复制内容的目标数组。
  • src -- 要复制的字符串。
  • n -- 要从源中复制的字符数。

2. 作用: 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用‘\0’填充

      9.2 实例

         当src>=n时:

#include <stdio.h>
#include <string.h>
int main()
{char src[40] = { 0 };char dest[12] = { 0 };strcpy(src, "This is runoob.com");strncpy(dest, src, 10);printf("最终的目标字符串:%s\n", dest);return 0;
}

输出结果:最终的目标字符串:This is ru 

         当src<n时

include <stdio.h>
#include <string.h>
int main()
{char src[40] = { 0 };char dest[12] ="this is";strcpy(src, "This");strncpy(dest, src, 10);printf("最终的目标字符串:%s\n", dest);return 0;
}

输出结果:最终的目标字符串:This 

     9.3实现strncpy()函数 

思路:总体实现思路和strcpy()差不多,但是要分src<n时,src>=n两种情况讨论

char* my_strncpy(char* dest, const char* src, int n)
{assert(dest && src);char* ret = dest;    //将dest首地址储存在ret中,方便返回while (*src && n){*dest = *src;dest++;src++;n--;}if (n != 0)//n>src{while (n){*dest = '\0';dest++;n--;}}return ret;  //返回数组的首地址
}

  10.strncat()函数 

       10.1用法

1 .声明:char *strncat(char *dest, const char *src, size_t n)

  • dest -- 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串,包括额外的空字符。
  • src -- 要追加的字符串。
  • n -- 要追加的最大字符数。

2. 用法:把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。

  如果n<src的长度,将source指向字符串的前n个字符追加到dest指向的字符串末尾,再追加⼀个 \0 字符。

  如果n>=src,只会将字符串中到\0 的内容追加到dest指向的字符串末尾

3. 返回值:该函数返回一个指向最终的目标字符串 dest 的指针。

       10.2 实例

         当n<src时:

#include<string.h>
int main()
{char str1[20];char str2[20];strcpy(str1, "hello ");strcpy(str2, "helloworld");strncat(str1, str2, 6);printf("%s\n", str1);return 0;
}

 输出结果:hello hellow

       当n>=src时:

#include<string.h>
int main()
{char str1[20];char str2[20];strcpy(str1, "hello ");strcpy(str2, "helloworld");strncat(str1, str2, 15);printf("%s\n", str1);return 0;
}

输出结果:hello helloworld 

   因为strncat()函数会自动在末尾补充'\0‘的优势,所以可以用于“自己对自己添加”

int main()
{char arr[10] = "abc";strncat(arr, arr, 3);printf("%s", arr);return 0;
}

       10.3实现strncat()函数

 思路:也和strcat()实现方法大致相同,但是要注意如果n<src时,要在末尾自动补充'\0'哦

   代码实现: 

#include<assert.h>
char* my_strncat(char* dest, const char* src, int n)
{char* ret = dest;  //将dest首地址储存在ret中assert(dest&&src);  //保证dest、src非空while (*dest != '\0')//找到dest结尾的‘\0’{dest++;}while (n && (*dest++ = *src++))//把src里的字符一个个放入dest后{n--;   //循环跳出条件}if(n==0){*dest = '\0'; //如果n<src}return ret; //返回dest字符串起始地址
}

  11.strtok()函数 

    11.1用法

1. 声明:char *strtok(char *str, const char *delim)

  • str -- 要被分解成一组小字符串的字符串。第一次调用 strtok() 时,这个参数应该是你想要分割的字符串。随后的调用应该将此参数设置为NULL,以便继续从上次的位置分割。
  • delim -- 包含分隔符的 C 字符串。

2. 作用:strtok() 用于将字符串分割成一系列的子串

3. 返回值:该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。

   贝蒂说:“如果一下子没有明白,不要但心,继续跟着贝蒂看看例子就明白啦~” 

    11.2 实例

#include <string.h>
#include <stdio.h>
int main() 
{char str[80] = "This is - betty -@class";const char s[] = "-@";char* p = strtok(str, s);//第一次传参while (*p != NULL){printf("%s\n", p);p = strtok(NULL, s);//非第一次传参}return 0;
}

输出结果:

This is
 betty
 class 

   11.3 实现strtok()函数

      在这里这里,贝蒂详解strcat哦~

12.strerror()函数

    12.1用法

1. 声明:char *strerror(int errnum)

  • errnum -- 错误号,通常是 errno

2. 作用:从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。strerror 生成的错误字符串取决于开发平台和编译器。

2. 返回值:该函数返回一个指向错误字符串的指针,该错误字符串描述了错误 errnum。

   12.2实例

#include<string.h>
#include<errno.h>//错误码的头文件
int main()
{//每一个错误码对应一个错误信息printf("%s\n", strerror(0));printf("%s\n", strerror(1));printf("%s\n", strerror(2));printf("%s\n", strerror(3));return 0;
}

输出:

No error  (没有错误)
Operation not permitted (操作不允许)
No such file or directory (没有这样的文件)
No such process (没有这样的进程)

     strerror常常用于动态内存开辟和文件的操作 

FILE* pfwrite = fopen("contact.txt", "wb");
if (pfwrite == NULL)
{printf("%s\n", strerror(errno));//如果打开失败,输出错误信息return 1;
}
pc->data = (peoinfo*)calloc(3 , sizeof(peoinfo));
if (pc->data == NULL)
{printf("inticontact:%s\n", strerror(errno));//如果开辟失败,输出错误信息return 1;
}

        贝蒂说:“可惜贝蒂现在已学的知识,还无法实现strerror()啦”

(二)内存操作函数

 1. 简介

     除了字符函数和字符串函数,<string.h>中还有一类内存操作函数,如memset(),memcmp()等函数,他们在功能和某些字符串函数很像,但作用范围更广,除了作用于字符串外,还可以作用于int ,double等类型,但因为是以字节为单位改变,所以限制也很大。下面就让我们来看看吧~

 2. memset()函数

   2.1用法

1. 声明:void *memset(void *str, int c, size_t n)

  • str -- 指向要填充的内存块。
  • c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
  • n -- 要被设置为该值的字符数。

2. 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。

3. 返回值:该值返回一个指向存储区 str 的指针。

2.2实例

#include <stdio.h>
#include <string.h>
int main()
{char str[] = "hello world";memset(str, 'x', 6);//以字节为单位printf(str);return 0;
}

输出结果:xxxxxxworld 

int main()
{int arr[4] = { 1,2,3,4 };memset(arr, 1, sizeof(arr));int i = 0;for (i = 0; i < 4; i++){printf("%d ", arr[i]);}return 0;
}

 输出结果:16843009 16843009 16843009 16843009

贝蒂说:“虽然memset可以作用于int,float等类型,但是memset设置是以字节为单位,容易造成不符合我们预期的情况哦~”

2.3 实现memset() 

思路:memset()函数和strcpy()函数有点像,都是替换,但是内在实现也有区别,因为memset()函数还可以用于不同数据类型,所以要先强制类型为(char*),再以字节为单位替换。

代码实现:

#include <string.h>
#include<assert.h>
void* my_memset(void*str, int c, size_t n)
{assert(str);//防止str为空指针char* tmp= (char*)str;//以字节为单位改变while (n--){*tmp = c;tmp++;}return str;
}
int main()
{char str[] = "hello world";my_memset(str, 'x', 6);//以字节为单位printf(str);return 0;
}

贝蒂说:“memset()函数常用于初始化哦~” 

3.  memcmp()函数

3.1用法

1. 声明:int memcmp(const void *str1, const void *str2, size_t n)

  • str1 -- 指向内存块的指针。
  • str2 -- 指向内存块的指针。
  • n -- 要被比较的字节数。

2. 作用:把存储区 str1 和存储区 str2 的前 n 个字节进行比较。

3. 返回值:

  • 如果返回值 < 0,则表示 str1 小于 str2。
  • 如果返回值 > 0,则表示 str1 大于 str2。
  • 如果返回值 = 0,则表示 str1 等于 str2。

 3.2 实例

#include <stdio.h>
#include <string.h>
int main() 
{char str1[] = "Hello, World!";char str2[] = "Hello, World!";char str3[] = "Hello, Betty!";// 比较相同的字符串if (memcmp(str1, str2, strlen(str1)) == 0){printf("str1 和 str2 相同。\n");}// 比较不同的字符串if (memcmp(str1, str3, strlen(str1)) != 0) {printf("str1 和 str3 不同。\n");}return 0;
}

 输出:

str1 和 str2 相同。
str1 和 str3 不同。

3.3 实现memcmp() 

思路:总体思路与strncmp差不多,只是也需要先强制类型转换

#include<stdio.h>
#include<assert.h>
int my_memcmp(const void* str1, const void* str2, size_t n)
{assert(str1 && str2);//char* p1 = (char*)str1;char* p2 = (char*)str2;while (n--&&(*p1==*p2)){if (*p1 == '\0'){return 0;}p1++;p2++;}return *p1 - *p2;
}

贝蒂说:“memcmp()函数也是以字节比较,所以限制也很大哦~” 

3.4strcmp,strncmp,memcmp之间的区别 

  • memcmp是比较两个存储空间的前n个字节,即使字符串已经结束,仍然要比较剩余的空间,直到比较完n个字节。
  • strcmp比较的是两个字符串,任一字符串结束,则比较结束。
  • strncmp在strcmp的基础上增加比较个数,其结束条件包括任一字符串结束和比较完n个字节。
  • strcmp比较的字符串,而memcmp比较的是内存块,strcmp需要时刻检查是否遇到了字符串结束的 /0 字符,而memcmp则完全不用担心这个问题,所以memcmp的效率要高于strcmp

4. memcpy()函数 

4.1用法

1. 声明:void *memcpy(void *str1, const void *str2, size_t n)

  • str1 -- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
  • str2 -- 指向要复制的数据源,类型强制转换为 void* 指针。
  • n -- 要被复制的字节数。

2. 作用:从存储区 str2 复制 n 个字节到存储区 str1

3. 返回值:该函数返回一个指向目标存储区 str1 的指针。

 4.2 实例

int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };memcpy(arr2, arr1, 20);//前五个元素int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr2[i]);}return 0;
}

输出结果:1 2 3 4 5 0 0 0 0 0 

4.3实现memcpy() 

思路:自然也是和strcpy()差不多啦,嘻嘻

void* my_memcpy(void* dest, const void* src, size_t n)
{assert(dest && src);//防止空指针void* ret = dest;while (n--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;
}

贝蒂说:“因为memcpy()函数实现机制,所以不能自己对自己进行拷贝哦~” 

4.4strcpy,strncpy,memcpy之间的区别

  1. memcpy是从源存储空间拷贝到目标存储空间;而strcpy,strncpy是从源字符串拷贝到目标字符串。
  2. memcpy拷贝时是按照参数n作为结束标志的,即拷贝n个字节就结束;strncpy是以参数n或者‘\0’为结束标志;strcpy是判断‘\0’为结束标志。

5. memmove()函数

5.1用法

    我们上面说过memcpy()无法对自己进行拷贝,那有没有能对自己拷贝的函数呢,当然有啦,就是我们的memmove()函数。

1. 声明:void *memmove(void *str1, const void *str2, size_t n)

  • str1 -- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
  • str2 -- 指向要复制的数据源,类型强制转换为 void* 指针。
  • n -- 要被复制的字节数。

2. 作用:从 str2 复制 n 个字符到 str1,但是在重叠内存块这方面,memmove() 是比 memcpy() 更安全的方法。如果目标区域和源区域有重叠的话,memmove() 能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。如果目标区域与源区域没有重叠,则和 memcpy() 函数功能相同。

3. 返回值:该函数返回一个指向目标存储区 str1 的指针。

 5.2实例

#include <stdio.h>
#include <string.h>
int main() 
{char str[] = "Hello, World!";printf("Original string: %s\n", str);// 将字符串前6个字符移动到字符串的末尾memmove(str, str + 7, 6);printf("Modified string: %s\n", str);return 0;
}

输出结果:

Original string: Hello, World!
Modified string: World! World!

5.3实现memmove()

 分析如下:

  情况1:

//假设需要拷贝以下场景
int arr1[9] = { 1,2,3,4,5,6,7,8,9 };
my_memmove(arr1, arr1 + 2, 12);

  1. 假设我们从3的位置开始拷贝,3-1,4-2,5-3,拷贝成功。

  2. 假设我们从5的位置开始拷贝,5-3,4-2,5-1,拷贝失败。

 情况2: 

//假设我们要拷贝的情况如下
int arr1[9] = { 1,2,3,4,5,6,7,8,9 };
my_memmove(arr1+4, arr1 + 2, 12);

 1. 假设我们从3的位置开始拷贝,3-5,4-6,3-7,拷贝失败。

 2. 如果我们从5的位置开始拷贝,:5-7,4-6,3-5,拷贝成功。

 情况3: 

int arr1[9] = { 1,2,3,4,5,6,7,8,9 };
my_memmove(arr1+1, arr1 + 5, 12);
my_memmove(arr1 + 5, arr1 + 1, 12);

1. 假设从6开始拷贝,6-2,7-3,8-4,拷贝成功。

2. 假设从8开始拷贝,8-4,7-3,6-2,拷贝成功。

1. 假设从2开始拷贝,2-6,3-7,4-9,拷贝成功。

2. 假设从4开始拷贝,4-8,3-7,2-6,拷贝成功。

总结:如果dest字符串在src的字符左边,则从首元素拷贝。如果dest字符串在src右边,则从末尾元素开始拷贝。

代码实现:

void* my_memmove(void* dest, const void* src, size_t n)
{assert(dest && src);//防止空指针void* ret = dest;if (dest <= src)//dest在src左侧{while (n--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}}else//dest在src的右侧{dest = (char*)dest + n - 1;//指向末尾src = (char*)src + n - 1;//指向末尾while (n--){*(char*)dest = *(char*)src;dest = (char*)dest - 1;src = (char*)src - 1;}}return ret;
}

结言:

   第一次写这么长的博客,累死贝蒂啦,虽然没有介绍完string.h中所有的库函数,但是也把常用的基本介绍啦,希望这些能对大家有所帮助哦~

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

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

相关文章

PhpStorm下载、安装、配置教程

前面的文章中&#xff0c;都是把.php文件放在WampServer的www目录下&#xff0c;通过浏览器访问运行。这篇文章就简单介绍一下PhpStorm这个php集成开发工具的使用。 目录 下载PhpStorm 安装PhpStorm 配置PhpStorm 修改个性化设置 修改字符编码 配置php的安装路径 使用Ph…

网络基础3

NAT&#xff08;Network Address Translation&#xff09;&#xff1a;网络地址转换 通过将内部网络的私有IP地址装换成全球唯一的公网IP地址&#xff0c;使内部网络可以连接到互联网。 广域网就是外网&#xff0c;局域网就是内网 私有IP地址&#xff1a;&#xff08;如果是纯内…

Flask基本用法:一个HelloWorld,搭建服务、发起请求

目录 1、简介 2、安装 3、Flask使用示例 参考 1、简介 官网文档 Flask是一个轻量的web服务框架&#xff0c;我们可以利用它快速搭建一个服务&#xff0c;对外提供接口&#xff0c;其他人可以轻松调用我们的服务。这对算法工程师来说比较关键&#xff0c;我们通常不擅长搞开发…

极坐标下的牛拉法潮流计算14节点MATLAB程序

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 潮流计算&#xff1a; 潮流计算是根据给定的电网结构、参数和发电机、负荷等元件的运行条件&#xff0c;确定电力系统各部分稳态运行状态参数的计算。通常给定的运行条件有系统中各电源和负荷点的功率、枢纽…

JRT实现原生Webservice发布

之前准备试试Java发布Webservice&#xff0c;开始以为很简单&#xff0c;因为C#发布很简单。后面发现太费劲了&#xff0c;依赖一堆包&#xff0c;下面几种都试了一下&#xff1a; JAX-WS (Java API for XML Web Services)&#xff1a;这是Java EE平台的标准&#xff0c;用于创…

nodejs微信小程序+python+PHP的微博网络舆情分析系统-计算机毕业设计推荐

&#xff08;4&#xff09;微博信息交流&#xff1a;在首页导航栏上我们会看到“微博信息交流”这一菜单&#xff0c;我们点击进入进去以后&#xff0c;会看到所有管理员在后台发布的交流信息&#xff1b; &#xff08;5&#xff09;新闻资讯&#xff1a;用户可以查看新闻资讯信…

【STM32入门】4.2对射红外传感器计次

1.接线方式 主要是编写传感器的驱动、配合OLED&#xff0c;每遮挡对射红外传感器&#xff0c;OLED屏幕的计数就加一。 2.驱动编写 首先新建.c文件和.h文件&#xff0c;命名为CountSensor 国际惯例&#xff0c;.c文件内要包含stm32.h头文件&#xff0c;然后编写 CountSensor_…

在Linux上安装配置Nginx高性能Web服务器

1 前言 Nginx是一个高性能的开源Web服务器&#xff0c;同时也可以作为反向代理服务器、负载均衡器、HTTP缓存以及作为一个邮件代理服务器。它以其出色的性能和灵活性而闻名&#xff0c;被广泛用于处理高流量的网站和应用程序。本文将介绍在Linux环境中安装Nginx的步骤&#xf…

new一个对象

1.自己直接调用 function Person(name, age) {this.name name;this.age age;}let a1 new Person("小明", 20);let a2 new Person("小菜", 25);console.log(a1); 打印的对象: 2.自己模拟一个 function Person(name, age) {this.name name;this.age a…

[Linux] LVS负载均衡群集——DR模式

一、 DR模式的特点 直接路由&#xff1a; 在LVS_DR模式下&#xff0c;负载均衡器不修改数据包的IP地址&#xff0c;只修改目的MAC地址。这使得数据包可以直接路由到后端实际服务器上&#xff0c;而不需要返回到负载均衡器。 高性能&#xff1a; 由于数据包在传输过程中不需要回…

本地运行大语言模型并可视化(Ollama+big-AGI方案)

目前有两种方案支持本地部署&#xff0c;两种方案都是基于llamacpp。其中 Ollama 目前只支持 Mac&#xff0c;LM Studio目前支持 Mac 和 Windows。 LM Studio&#xff1a;https://lmstudio.ai/ Ollama&#xff1a;https://ollama.ai/download 本文以 Ollama 为例 step1 首先下…

STM32_启动流程详解

目录标题 前言 启动流程概述复位中断函数详解SystemInit函数详解 __main函数详解 附录 stm32单片机的存储器映像中断向量表的映射 前言 最近在学习IAP远程OTA升级单片机固件程序&#xff0c;发现自己对单片机的启动流程还不是那么了解&#xff0c;就总结整理一下吧。 启动流程…

QT实现四则运算计算器

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setMaximumSize(240,300);this->setMinimumSize(240,300);this->setWindowTitle("计算器&…

node.js mongoose简述

目录 官方文档 mongoose Schema Model Query document 关系 官方文档 Mongoose v8.0.3: Getting Started mongoose Mongoose 是一个 Node.js 环境下 MongoDB 的对象建模工具。它提供了一种在应用程序中与 MongoDB 数据库进行交互的方式&#xff0c;使得开发者能够使用…

NoSQL 数据库有哪些典型应用?

前面的内容介绍了数据库读写分离和分库分表相关知识&#xff0c;都是针对关系型数据库的&#xff0c;即通常说的 RDBMS。除了关系型数据库&#xff0c;NoSQL 在项目开发中也有着越来越重要的作用&#xff0c;与此同时&#xff0c;NoSQL 相关的内容也是面试的常客。今天我们一起…

函数难题:排列

给定一个整数 n&#xff0c;将数字 1∼n 排成一排&#xff0c;将会有很多种排列方法。 现在&#xff0c;请你按照字典序将所有的排列方法输出。 输入格式 共一行&#xff0c;包含一个整数 n。 输出格式 按字典序输出所有排列方案&#xff0c;每个方案占一行。 数据范围 …

【Linux】驱动

驱动 驱动程序过程 系统调用 用户空间 内核空间 添加驱动和调用驱动 驱动程序是如何调用设备硬件 驱动 在计算机领域&#xff0c;驱动&#xff08;Driver&#xff09;是一种软件&#xff0c;它充当硬件设备与操作系统之间的桥梁&#xff0c;允许它们进行通信和协同工作。驱动程…

[已解决】uniapp内置插件,editor富文本报错(附quill.min.js、image-resize.min.js文件)

在使用uni-app运行内置插件editor时&#xff0c;无法输入内容&#xff0c;控制台报错 原因&#xff1a;查看官网得知&#xff0c;需动态引入quill.min.js、image-resize.min.js文件 解决方法&#xff1a; 1.下载quill.min.js、image-resize.min.js到项目static/eidtor文件中 链…

云原生之深入解析Kubernetes Operator的最佳实践和最常见的问题分析

一、Kubernetes Operator 简介 Kubernetes Operator 是通过连接主 API 并 watch 时间的一组进程&#xff0c;一般会 watch 有限的资源类型。当相关 watch 的 event 触发的时候&#xff0c;operator 做出响应并执行具体的动作。这可能仅限于与主 API 交互&#xff0c;但通常会涉…

Linux下FFmepg使用

1.命令行录一段wav,PCM数据 ffmpeg -f alsa -i hw:0,0 xxx.wav//录制 ffplay out.wav//播放ffmpeg -f alsa -i hw:0,0 -ar 16000 -channels 1 -f s16le 1.pcm ffplay -ar 16000 -channels 1 -f s16le 1.pcm -ar freq 设置音频采样率 -ac channels 设置通道 缺省为1 2.将pcm…