C语言进阶(五)——字符串+内存函数的介绍

一、字符串函数的介绍

前言

  C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串或者字符数组中。字符串常量适用于那些对他不做修改的字符串函数。

1.strlen函数的介绍及模拟实现


(1)strlen函数的使用

#include <stdio.h>
#include <string.h>
int main()
{
	//字符串函数的实现
   //字符串函数的功能
	char a[6] = "abcde";
	
	int len = strlen(a);
	
	printf("%d\n", len);
	
	return 0;
}

执行效果如下:
在这里插入图片描述

(2)strlen函数功能

通过msdn函数功能查找得知strlen具体使用情况如下

在这里插入图片描述
在这里插入图片描述

  strlen的功能是获取字符串的长度,函数内部传的参数类型是 char *,返回的类型是size_t类型。返回值是字符串中字符出现的个数。

在这里插入图片描述

(3)strlen的模拟实现


size_t my_strlen(const char* p)
{
	int count = 0;
	while (*p)
	{
		count++;
		p++;
	}
	return count;
}

(4)易错点

注意:

#include <stdio.h>
#include <string.h>
int main()
{
     const char*str1 = "abcdef";
     const char*str2 = "bbb";
     if(strlen(str2)-strlen(str1)>0)
   {
      printf("str2>str1\n");
   } 
    else
   {
      printf("srt1>str2\n");
   }
    return 0; 
 }

  如果按照我们平时的理解,我们所算的strlen(str1)=6,strlen(str2)=3,strlen(str2)—strlen(str1)<0,会打印str1>str2。但是实际上打印的结果却并不是我们想的那样。

在这里插入图片描述
  打印结果为str2>str1.那么我们想是strlen(str2)— strlen(str1)>0吗?并不是的,还记得我们说的strlen的返回类型是size_t 无符号数,是不存在负数的,减到小于0是会成为一个很大很大的数字,此时结果还是大于0,所以会出现上述打印的结果,这点很容易出现错误。牢记strlen的返回类型是无符号整型。

2.strcpy函数的介绍及模拟实现


(1)strcpy函数的使用

#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[30] = "*********************";
	char arr2[] = "hello world";
	/*strcpy(arr1,arr2);*/
	printf("%s\n", strcpy(arr1, arr2));
	return 0;
}

执行代码效果如下:
在这里插入图片描述

(2)strcpy函数功能

通过msdn函数功能查找得知strlen具体使用情况如下
在这里插入图片描述
在这里插入图片描述

  我们得知,strcpy是字符串拷贝函数,他的功能就是拷贝字符串,函数内部的参数:第一个参数是目标字符串,第二个参数是 char* str Source (起始字符串),最后将起始字符串拷贝到目标字符串中,达到最终效果。函数返回类型是 char*。返回值是目标字符串的起始地址处。

在这里插入图片描述

(3)strcpy 函数的模拟实现

#include <stdio.h>
#include <string.h>
#include <assert.h>

char * my_strcpy(char *dest,const char *src)
{
	char * p = dest;
	assert(dest && src );
	while (*src)
	{
	   *dest=*src;
		dest++;
		src++;
	}
	*dest=*src;
	return p;
}

int main()
{
	char arr1[30] = "*********************";
	char arr2[] = "hello world";
	/*strcpy(arr1,arr2);*/
	printf("%s\n", strcpy(arr1, arr2));
	return 0;
}

  这里的模拟实现中有一点需要注意的,我们将源字符串拷贝到目标字符串后,函数要返回目标字符串的起始指针的地址处,但是dest已经++,所以在一开始我们要将dest的初始值保存起来,char *p=dest,最后返回p。


3.strcat函数的介绍及模拟实现


(1)strcat函数的使用

int main()
{
	char arr1[20] = "hello ";
	char arr2[20] = "world";
	/*my_strcat(arr1, arr1)*/
		printf("%s\n", strcat(arr1, arr2));
	return 0;
}

代码实现效果:
在这里插入图片描述

(2)strcat函数功能及使用

通过msdn函数功能查找得知strlen具体使用情况如下
在这里插入图片描述
在这里插入图片描述

  该函数的功能是追加一个字符串,函数内部的参数:第一个参数是char*str Destination(目标字符串),第二个参数是 起始字符串,将源字符串追加到目标字符串后。返回类型是char *。返回的是目标字符串的起始地址处。

在这里插入图片描述

(3)strcat字符追加函数的模拟实现

  首先我们问一个问题,如何实现字符追加,首先第一步先要找到目标字符串的’\0 ‘,然后将目标函数的’ \0 ‘改为源字符串的初始指针,再将src赋给dest直到*src=’\0’,返回目标字符串的初始指针的地址处。

代码实现:

#include <stdio.h>
#include <assert.h>
#include <string.h>

char*  my_strcat(char*dest, const char*src)
{
	assert(dest && src);
	char *ret = dest;
	//1.找到目标空间的\0
	while (*dest)
	{
		dest++;
	}
	
	//2.追加
	while ( *src)
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;
	return ret;
}

int main()
{
	char arr1[20] = "hello ";
	char arr2[20] = "world";
	/*strcat(arr1, arr2)*/
	printf("%s\n", my_strcat(arr1, arr2));
	return 0;
}

(4)字符串能否给自己追加本身?

  关于字符追加函数能否给本身的字符串追加本身,我们可以根据上面的strcat模拟实现的函数进行分析。

回顾strcat字符追加函数的过程:

以下列代码举例

#include <stdio.h>
#include <string.h>
int main()
{
     char arr="abc";
     strcat(arr,arr);
     return 0;
}

在这里插入图片描述

4.strcmp函数的介绍及模拟实现


(1)strcmp函数的使用

注意
  strcmp函数比较的不是字符串的长度,而是字符串对应字符的ASCII码值

#include <stdio.h>
#include <string.h>

int main()
{
  char arr1[]="abcdef";
  char  arr2[]="abc";
     if(strcmp(arr1,arr2)<0)
           printf("arr1<arr2\n");
     else if(strcmp(arr1,arr2)>0)
           printf("arr1>arr2\n");
     else if(strcmp(arr1,arr2)==0)
           printf("arr1=arr2");
  return 0;
}

代码显示效果:
在这里插入图片描述

(2)strcmp函数功能

在这里插入图片描述
在这里插入图片描述

  strcmp函数的函数功能是比较两个字符串,那么如何进行比较呢?strcmp函数比较的是字符串对应的字符的ASCII码值,返回值是int类型的,分别向函数内部传入两个字符串s1、s2。

在这里插入图片描述

(3)strcmp函数的模拟实现

int my_strcmp(const char *s1, const char *s2)
{
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
			return 0;
		s1++;
		s2++;
	}
	if (*s1 > *s2)
		return 1;
	else if (*s1 < *s2)
		return -1;

//	return *s1 - *s2;
//}

注意:

  在模拟过程中我们可以定义 小于时返回-1,大于时返回1,相等时返回0。也可以直接返回*s1-*s2,字符串比较的效果一样。


5.长度受限制的字符串函数的使用


(1)strncpy函数的介绍

在这里插入图片描述
在这里插入图片描述
功能介绍:

1.拷贝count个字符从源字符串到目标空间。

2.如果源字符串的长度小于count,则拷贝完源字符串之后,在目标的后边追加0,直到count个。


(2)strncat函数的介绍

在这里插入图片描述
在这里插入图片描述
功能介绍:

1.将source的num个字符追加到destination

2.如果source中的字符串长度小于num,则只复制到结束空字符之前的内容。


实现效果如下:
在这里插入图片描述

(3) strncmp函数的介绍

在这里插入图片描述
在这里插入图片描述

功能介绍:

比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。


6.strstr函数的介绍和模拟实现


(1)strstr函数的使用

int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bcde";
	char * ret = strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("找不到字串\n");
	}
	else
	printf("%s\n",ret );
	return 0;
}

代码实现效果:
在这里插入图片描述

(2)strstr函数的功能

通过msdn函数功能查找得知strlen具体使用情况如下
在这里插入图片描述
在这里插入图片描述
  我们可得知strstr函数的功能是查找一个字串,或者截取一个字串。第一个参数传一个字符串,第二个参数传一个要查找的字符串。返回类型是char *,如果查找到了,那么返回字符串中查找到的那个字符的指针,直到\0结束。如果未查找到,那么就返回NULL。


(3)strstr函数的模拟实现

#include <stdio.h>
#include <string.h>
#include <assert.h>

char * my_strstr(char * s1, char * s2)
{
	assert(s1&&s2);
	if (*s2 == '\0')
		return s1;
	char * cp = s1;
	while (*s1 != '\0')
	{	
		char *p2 = s2;
		char *p1 = cp;
		while (*p1 == *p2)
		{
			p1++;
			p2++;
		}
		if (*p2 == '\0')
		{
			return cp;
		}	
		cp++;

	}

	return NULL;
}

7.strtok函数的介绍

strtok是一个用来分隔字符串的函数。
在这里插入图片描述
在这里插入图片描述

下面给一个例子来演示strtok的具体功能

#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[] = "crq@2745131427qq.com";
	char arr2[30] = { 0 }; 
	char p[] = "@.#";  //  分隔符的集合的字符串
	strcpy(arr2, arr1);
	printf("%s\n", strtok(arr1, p));
	printf("%s\n", strtok(NULL, p));
	printf("%s\n", strtok(NULL, p));
	return 0;
}

代码显示效果:
在这里插入图片描述
好了,那么strtok函数,我们应该怎样使用呢?

对于第一、二条规则,我们用一个字符串来记录分隔符的集合。(以分隔符为标记,从而进行分割字符串操作)

strtok操作会对字符串进行修改,所以我们要拷贝内容到另一个字符串中。

以上面的例子代码为例,strtok 的第一个参数不为NULL,我们进行 strtok(arr1,p),将第一个分隔符@,改为\0。同时函数保存了第一个分隔符的位置。

第二次传参为NULL,我们进行 strtok(NULL,p),此时的NULL虽然传了一个空指针,但是指向了上一次保存的分隔符的位置。从这个位置开始,将下一个分隔符.,改为\0。同时函数保存此位置。

依次进行该操作…

  可能有同学会问了:函数内部的变量出函数不是销毁了么,函数怎么保存一个地址变量呢?

我们猜测:在C语言关键字的学习中,我们学到了一个static 的关键字,出了函数也能保存下来。可能这个函数的实现过程中存在一个static关键字,所以每次的标记位置得以保存…

  但是我们如果不知道字符串内部有多少分隔符,而且strtok(NULL,p)的操作重复多次,显得冗余,如何进行简化呢?

给出的解决冗余方案:

#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[] = "crq@2745131427qq.com";
	char arr2[30] = { 0 }; 
	char p[] = "@.#";  //  分隔符的集合的字符串
	strcpy(arr2, arr1);
	char *ret = NULL;
	for (ret = strtok(arr1, p); ret != NULL; ret = strtok(NULL, p))
	{
		printf("%s\n",ret);
	}
	/*printf("%s\n", strtok(arr1, p));*/
	//printf("%s\n", strtok(NULL, p));
	//printf("%s\n", strtok(NULL, p));
	return 0;
}

8.strerror函数的介绍

在这里插入图片描述
该函数相关信息如下:
在这里插入图片描述
功能介绍:

错误报告函数,把错误码转换为对应的错误信息,返回错误信息对应的字符串的起始地址。

那么我们平时如何使用呢?

首先引入头文件 <error.h>

#include <stdio.h>
#include <error.h>

int main()
{
FILE *pf=fopen("test.txt","r");
if(pf==NULL)
printf("fopen%s\n",strerror(error));
return  0;
}

实现效果:

在这里插入图片描述


9.字符操作函数


(1)字符分类函数

在这里插入图片描述


(2)字符转换函数

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
具体这里就不做更多介绍…


二、内存操作函数介绍

  上述函数都是字符串或字符操作函数,那么如果我们想要拷贝一个整形数组,或者其他类型的数据,我们不能用字符串操作函数时,我们应该怎样拷贝呢?

  在这里,我们引入内存操作函数的概念,我们直接对数据的内存进行操作。


1.memcpy函数的介绍和模拟实现


(1)memcpy函数的功能

在这里插入图片描述
在这里插入图片描述

memcpy函数的作用:在两个内存缓冲区进行拷贝数据

返回dest的起始地址处

我们发现memcpy函数的前两个参数都是void * 类型的,第三个参数是拷贝的字节数。

在这里插入图片描述

(2)memcpy函数的使用


#include <stdio.h>
#include <memory.h>

int main()
{
	int arr1[] = { 1, 2, 3, 4, 5 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr1, sizeof(arr1));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d  ", arr2[i]);
	}
	return 0;
}

代码实现效果:

在这里插入图片描述

(3) memcpy函数的模拟实现


void * my_memcpy(void * dest, void * src, int num)
{
	char*ret = (char *)dest;
	while (num--)
	{
		*(char *)dest = *(char *)src;

		++(char *)dest;
		++(char *)src;
	}
	return ret;
}

结构体数据的拷贝过程如下:

struct S
{
	char name[20] ;
	int age;
	
};

void * my_memcpy(void * dest, void * src, int num)
{
	char*ret = (char *)dest;
	while (num--)
	{
		*(char *)dest = *(char *)src;

		++(char *)dest;
		++(char *)src;
	}
	return ret;
}

int main()
{
	struct S arr1[] = { { "chen", 11 }, {"hai",20} };
	struct S arr2[10] ;
	my_memcpy(arr2, arr1, sizeof(arr1));
	int i = 0;
	for (i = 0; i < 2; i++)
	{
		printf("%s %d ", arr2[i].name,arr2[i].age);
	}
	return 0;
}

实现效果如下
在这里插入图片描述

(4)memcpy函数的缺点

现在有一个要求:
在这里插入图片描述
在这里插入图片描述
  所以,我们并不能按照我们的要求打印,内存相互重叠的情况下,内存的数字会发生改变。而在memmove 函数中则完美的解决了这个问题(内存重叠)。


2.memmove函数的介绍和模拟实现


(1)memmove函数的功能

  memmove和memcopy 功能 和 函数的参数 都大部分相同
在这里插入图片描述
区别的是:
在这里插入图片描述

(2)memmove函数的模拟实现

首先我们要明确怎样拷贝不影响内存重叠

在这里插入图片描述
将 2,3,4,5 拷贝到 4,5,6,7。

这种情况下 src<dest, 我们将src中的数据从后向前进行拷贝,就可以避免内存重叠的影响。

将 6,7,8,9 拷贝到4,5,6,7.

这种情况下 src>dest,我们将src中的数据从前向后进行拷贝,可以避免内存重叠的影响。

我们模拟实现时,考虑如何从后向前拷贝,如何从前向后拷贝。

#include <assert.h>
void* my_memmove(void * dest, void *src, int num)
{
	char * ret = (char*)dest;
	assert(dest&&src);
	if (src > dest)
	{
		//从前向后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			++(char*)dest;
			++(char*)src;
		}
	}
	else if (src < dest)
	while (num--)
	{
		*((char*)dest + num) = *((char*)src + num);

	}

	return ret;
}

代码实现效果:
在这里插入图片描述
避免了内存重叠,成功实现!!


3.memcmp函数的介绍

在这里插入图片描述
在这里插入图片描述

(1)memcmp函数的功能

对各种类型进行比较

(2)memcmp函数的使用

例:整形数据的比较

int main()
{

	int arr1[] = { 1, 5, 6, 7, 8, 9 };
	int arr2[] = { 1, 2, 3, 4, 5, 6 };
	int ret=memcmp(arr1, arr2, 8);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述

4.memset函数的介绍

在这里插入图片描述
在这里插入图片描述

(1)memset函数功能

  函数的三个参数(目标的起始指针处,设置的字符,设置修改字符的字节),将目标的字符串或者其他数据改为设置的字符或其他类型。


(2)memset函数的使用

int main()
{
	char arr[] = "###########";
	memset(arr, '*', 5);
	printf("%s\n", arr);

}

在这里插入图片描述




好了,内存+字符串函数的说明就介绍到这里,希望大家多多练习,谢谢欣赏!!






未完待续!!



C语言进阶(六)——自定义类型详解(结构体+枚举+联合)已更新

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RAIN 7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值