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

news2024/11/15 21:24:11

目录

引言:

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

 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与str2
	int ret2 = strcmp(str1, str3);//比较str1与str3
	int ret3 = strcmp(str1, str4);//比较str1与str4
	printf("%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的内容拷贝去des
	printf("%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.coloradmin.cn/o/1323629.html

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

相关文章

Java:语法速通

参考 菜鸟教程 java 继承 class 父类 { }class 子类 extends 父类 { }继承的特性&#xff1a; 子类拥有父类非private的属性和方法子类可以对父类进行扩展子类可以重写父类的方法使用extends只能单继承&#xff0c;使用implements可以变相的多继承&#xff0c;即一个类继承…

P71自监督式学习

命名都以芝麻街的角色命名 x 分为x’ 和 x’’ &#xff0c;自己跟自己学bert 架构跟 transformer Encoder 一样&#xff0c;输入一排向量&#xff0c;输出一排向量&#xff0c;一般用在自然语言处理上 模型大小&#xff1a; x 分为 x’ x’’ 自学习 bert 可以做输入一排向…

未来之笔:AI绘画如何重新定义人物设计艺术

随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;艺术创作领域正迎来一场革命性的变革。AI绘画正在重新定义人物设计艺术&#xff0c;为艺术家和设计师带来前所未有的创作可能性。人物设计作为艺术创作中的重要组成部分&#xff0c;正在受到AI技术革新的深刻…

前端-如何自己做一个可视化的人员选择泛用组件

一、展示组件效果 二、组件的功能 1.用户可以在搜索框里输入字符&#xff0c;下方列表实时变动&#xff08;筛选出包含搜索字符的可选项&#xff09;&#xff0c;如果没有字符&#xff0c;就展示所有的选项 2.用户点击可选项&#xff0c;右侧出现标签 3.用户点击标签的XX&am…

【Git】Git基本操作

文章目录 Git 是什么Git 的优点Git 安装Linux UbuntuLinux CentOsWindows Git 基本操作1. 创建 Git 本地仓库2. 配置 Git3. Git工作区、暂存区和版本库4. 添加文件5. 查看 .git 文件6. 修改文件7. 版本回退 Git 是什么 Git是一个免费的、开源的分布式版本控制系统&#xff0c;…

国产Apple Find My认证芯片哪里找,伦茨科技ST17H6x芯片可以帮到您

深圳市伦茨科技有限公司&#xff08;以下简称“伦茨科技”&#xff09;发布ST17H6x Soc平台。成为继Nordic之后全球第二家取得Apple Find My「查找」认证的芯片厂家&#xff0c;该平台提供可通过Apple Find My认证的Apple查找&#xff08;Find My&#xff09;功能集成解决方案。…

Ubuntu 常用命令之 top 命令用法介绍

top命令是Linux下常用的性能分析工具&#xff0c;可以实时动态地查看系统中各个进程的资源占用状况&#xff0c;类似于Windows的任务管理器。它可以显示系统总的和分区的CPU使用率、内存使用率、交换区使用率、系统负载、进程数、最耗CPU的进程等信息。 top命令的参数如下 -b…

数据之门:使用IPIDEA开启网络自由之旅~

本文目录 前言一、网络代理IP简介二、IPIDEA 优势2.1 多种类型IP代理2.2 海量纯净代理池2.3 稳定高效数据收集架构2.4 个人IP管理中心 三、IP代理实操小Tips3.1 查看本地网络IP3.2 使用浏览器IP代理3.3 使用IPIDEA进行爬虫实操 四、总结 前言 各位友友&#xff0c;大家好&…

Win10电脑字体太浅了看不清的解决方法

在Win10电脑操作过程中&#xff0c;用户发现电脑字体太浅了&#xff0c;自己都看不清字体了&#xff0c;这样比较影响自己的操作效率。下面小编给大家带来Win10字体颜色太淡的解决方法&#xff0c;解决后用户可以看到Win10电脑字体颜色就不会太淡&#xff0c;视觉体验感也更好。…

22款奔驰E260L升级原厂360全景影像 效果分享

很多车友们会问个问题 就是升级这些功能会不会有异响&#xff1f;我们星骏汇回答这个问题 有异响是因为师傅经验不够安装的不够仔细 &#xff0c;像卡扣卡不到位 原厂预留位置 安装的不准确 可能就会造成异响&#xff0c;也有很多车友会问 为什么要升级呢&#xff1f;准确的来说…

众和策略:网络安全行业未来增速可期 多股获机构扎堆调研

工业和信息化部、国家网信办、人力资源和社会确保部等十四部分近来联合印发《关于打开网络安全技术运用试点演示作业的告诉》&#xff08;以下简称《告诉》&#xff09;&#xff0c;将以新式信息基础设备安全、数字化运用场景安全、安全基础才干进步为主线&#xff0c;遴选一批…

(1)(1.9) MSP (version 4.2)

文章目录 前言 1 协议概述 2 配置 3 参数说明 前言 ArduPilot 支持 MSP 协议&#xff0c;可通过任何串行端口进行遥测、OSD 和传感器。这样&#xff0c;ArduPilot 就能将遥测数据发送到 MSP 兼容设备&#xff08;如大疆护目镜&#xff09;&#xff0c;用于屏幕显示&#x…

生产问题(十二)GC垃圾回收导致空指针

一、引言 最近线上出现由于线程池任务执行超时导致的空指针&#xff0c;按道理说基本不会很多执行超时的&#xff0c;看了看监控&#xff0c;那个时间点发生了gc&#xff0c;gc时间超过5s。 说明这次是gc垃圾回收导致的问题&#xff0c;实际上需要排查解决的是垃圾回收问题。表…

element plus 表格el-table行多选单选

1 行多选-点击checkbox 添加一个 el-table-column&#xff0c;设 type 属性为 selection 即可 <template><div class"box"><el-table :data"tableData" selection-change"handleSelectionChange"><el-table-column type&…

vue3 使用<script lang=“ts“ setup>加上lang=“ts“后编译错误

报错信息 报错原因 加上了langts解决 下载typescript和loader npm install typescript ts-loader --save-dev配置vue.config.js 添加下面的代码 configureWebpack: { resolve: { extensions: [".ts", ".tsx", ".js", ".json"] }…

【Linux】冯诺依曼体系结构与操作系统及其进程

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解冯诺依曼体系结构与操作系统&#xff0c;掌握…

基础数据结构(1):链表

在学习算法时&#xff0c;发现用什么数据结构来存储数据是很重要的&#xff0c;所以学习数据结构也是必须的&#xff0c;先从基础数据结构&#xff1a;数组&#xff0c;字符串&#xff0c;链表&#xff0c;栈&#xff0c;队列&#xff0c;树&#xff0c;矩阵&#xff0c;邻接表…

3D小球跑酷

目录 一、前言 二、开发环境 三、场景搭建 1. 创建项目 2. 创建场景内物体 2.1 创建跑道 2.2 创建玩家 2.3 创建障碍物 2.4 改变跑道和障碍物的颜色 2.4.1 创建材质 2.4.2 给跑道和障碍物更换材质 四、功能脚本实现 1. 创建玩家脚本 2. 相机跟随 3. 胜负的判定 3…

spring boot版本升级遇到的一些问题

背景&#xff1a;由于项目需求&#xff0c;需要将nacos 1.4.6版本升级到2.x版本&#xff0c;由此引发的springboot、springcloud、springcloud Alibaba一系列版本变更。 旧版本分别为&#xff1a; Spring Boot 2.3.5.RELEASE Spring Cloud Hoxton.SR9 Spring Cloud Alibaba 2.2…

基于RocketMQ实现分布式事务

前言 在上一篇文章Spring Boot自动装配原理以及实践我们完成了服务通用日志监控组件的开发&#xff0c;确保每个服务都可以基于一个注解实现业务功能的监控。 而本文我们尝试基于RocketMQ实现下单的分布式的事务。可能会有读者会有疑问&#xff0c;之前我们不是基于Seata完成了…