各种字符串函数以及内存函数的讲解与实现

实现strlen()的三种方法

size_t my_strlen(char* arr)
{
    
    
	法1
	//int count = 0;
	//while (*arr)
	//{
    
    
	//	count++;
	//	arr++;
	//}
	//return count;
	//法2
	//if (*arr)
	//	return 1 + my_strlen(arr + 1);
	//else
	//	return 0;
	//法3
	//char* p = arr;
	//while (*arr)
	//{
    
    
	//	arr++;
	//}
	//return arr - p;
}

test1()
{
    
    
	char arr1[] = "abcdef";
	int len =my_strlen(arr1);
	printf("%d", len);
}
int main()
{
    
    
	test1();
}

比较简单,要注意的是法3中,指针-指针算的是指针之间的元素个数

实现字符串拷贝strcpy()

#include <assert.h>
char * my_strcpy(char*dest,const char*source)
{
    
    
	assert(dest&&source);
	char* p = dest;
	while (*dest++ = *source++)
	{
    
    
		;
	}
	return p;
}
test2()
{
    
    
	char arr1[] = "abcdef";
	char arr2[20] = "xxxxxxxxxxxxx";
	my_strcpy(arr2,arr1);
	printf("%s", my_strcpy(arr2, arr1));
}
int main()
{
    
    
	/*test1();*/
	test2();
}

实现讲解以及注意事项

  1. 源字符串必须以\0结束,意味着arr1数组不能没有只有a,b,c,d,e,f,必须包含\0
  2. 拷贝时会将源字符串中的\0拷贝到目标空间
  3. 目标空间必须足够大
  4. 目标空间必须可变,意味着不能给一个char*p来接收"xxxxxxxx",因为此时该字符串为常量字符串,不可被更改
  5. 由于源字符串不需要被修改,所以加上const更安全

实现字符串追加strcat()

#include <assert.h>
char* my_strcat(char* dest, const char* source)
{
    
    
	assert(dest&&source);
	char* p = dest;
	while (*dest)
	{
    
    
		dest++;
	}
	while (*dest++ = *source++)
	{
    
    
		;
	}
	return p;
}
test3()
{
    
    
	char arr1[] = "abcdef";
	char arr2[20] = "xxx\0xxxxxxxxxx";
	printf("%s", my_strcat(arr2, arr1));
}
int main()
{
    
    
	/*test1();*/
	/*test2();*/
	test3();
}

实现讲解以及注意事项

  1. 源字符串会找到目标字符串中的\0,并由此进行追加
  2. 源字符串和目标字符串都必须有\0
  3. 目标空间必须可更改
  4. 函数实现其实就是给目标字符串遍历找到\0+strcpy
  5. 不可以实现自己给自己追加,因为当自己给自己追加时,会把源字符串原来的\0覆盖掉,这样就会无限追加下去
  6. 使用strcat不可以进行自我追加

实现字符串比较strcmp()

int my_strcmp(char* str1, char* str2)
{
    
    
	while (*str1 == *str2)
	{
    
    
		if (*str1== '\0')
		{
    
    
			return 0;
		}
		str1++;
		str2++;
	}
	return *str1 - *str2;

}
test4()
{
    
    
	char arr1[] = "abcdefg";
	char arr2[20] = "abcdefg";
	printf("%d", my_strcmp(arr1, arr2));
}
int main()
{
    
    
	/*test1();*/
	/*test2();*/
	/*test3();*/
	test4();
}

实现讲解以及注意事项
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字

strncpy

在这里插入图片描述
如图可见,strncpy会将arr1的前5个字节拷贝到arr2中,不会把\0也拷贝进去,如果拷贝10个字节,将arr1的内容拷贝过去后,剩下的补\0
在这里插入图片描述

strncat

在这里插入图片描述
如图,strncat进行追加时会追加\0,并且可以实现自己给自己追加

strncmp

在这里插入图片描述
如图,即使是比较10个字节,还是比较到不同的字符截止,或到\0截止

strstr()

char* my_strstr(char* str1, char* str2)
{
    
    
	char* p = str1;
	char* s2 = str2;
	while (*p)
	{
    
    
		str1 = p;
		s2 = str2;
		while (*str1&&*s2&&*str1 == *s2)
		{
    
    
			str1++;
			s2++;
		}
		if (*s2 == '\0')
		{
    
    
			return p;
		}
		p++;
	}
	return NULL;
}
int main()
{
    
    
	char arr1[] = "aabbabcdefaa";
	char arr2[] = "abcdef";
	char* p = my_strstr(arr1,arr2);
	if (p == NULL)
	{
    
    
		printf("找不到");
	}
	else
		printf("%s", p);

}

实现讲解以及注意事项
在arr1中找arr2,找到了则返回arr1对应的地址,找不到则返回NULL

strtok

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char arr1[] = "abc'dwadfa;dadwdaw";
	char sp[] = "';";
	char* p = NULL;
	//printf("%s\n",p);
	//p = strtok(NULL, sp);
	//printf("%s\n", p);
	//p = strtok(NULL, sp);
	//printf("%s\n", p);
	//p = strtok(NULL, sp);
	//printf("%s\n", p);
	for (p = strtok(arr1, sp); p != NULL;p = strtok(NULL,sp))
	{
    
    
		printf("%s\n",p);
	}
}

实现讲解以及注意事项
函数的第一个参数是指定一个字符串,它包含有sp组成的多个分割符
第二个参数为一个包含分隔符字符串
第一次传被查找的字符串地址arr1,找到下一个标记即分隔符,将标记置为\0,返回标记的字符串的首元素地址,并保存这个标记的地址,下一次传NULL,从上一个保存的标记的地址之后继续查找,当查找时,找不到后面标记时,返回空指针

strerror

	FILE*pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
    
    
		printf("%s", strerror(errno));
		//perror("打开文件失败");
		return 1;
	}
	fclose(pf);
	pf = NULL;

若打开文件失败,返回NULL,当函数调用失败会将一个错误码放在errno中,strerror会将errno翻译成错误信息,返回错误信息的首地址,perror相当于strerror+printf()

memcpy

void* my_memcpy(void* s2, const void* s1, int num)
{
    
    
	void* ret = s2;
	while (num--)
	{
    
    
		*(char*)s2 = *(char*)s1;
		s2 = (char*)s2 + 1;
		s1 = (char*)s1 + 1;
	}
	return ret;
}
int main()
{
    
    
	double arr1[] = {
    
     1.1,2.3,4.4 };
	double arr2[10] = {
    
     0.0 };
	double*p = (double*)my_memcpy(arr2, arr1, 24);
	int i = 0;
	for (i = 0; i < 3; i++)
	{
    
    
		printf("%lf\n", p[i]);
	}
	return 0;
}

(char)s2 = (char)s1;
s2 = (char*)s2 + 1;
s1 = (char*)s1 + 1;
对于这一块代码,由于强制类型转换时临时的,也就是说* (char*)s2 = * (char*)s1;这个表达式完了之后,s2,s1又是void *
类型,需要再次强转,我们尽量不要对强转的变量进行++,–操作,容易出错,所以换了另一种写法
在这里插入图片描述

memcpy是内存块拷贝,可以拷贝任意类型,遇到\0也不会停止
第一个参数是目的地,第二个是源头,第三个是拷贝多少个字节
不能拷贝本身
它的缺陷是不能完成对自身的拷贝

memmove

void* my_memmove(void* s2,void* s1,int num)
{
    
    
	void* ret = s2;
	if (s2 < s1)
	{
    
    
		while (num--)
		{
    
    
			*(char*)s2 = *(char*)s1;
			s2 = (char*)s2 + 1;
			s1 = (char*)s1 + 1;
		}
	}
	else
		while (num--)
		{
    
    
			*((char*)s2 + num) = *((char*)s1 + num);
		}
}
int main()
{
    
    
	int arr1[] = {
    
     1,2,4,5,7,8,9,10 };
	my_memmove(arr1, arr1+2, 12);
	int i = 0;
	for(i =0; i < sizeof(arr1)/sizeof(arr1[0]);i++)
	{
    
    
		printf("%d ", arr1[i]);
	}
	return 0;
}

在这里插入图片描述
当source>dest时,为了防止sorce中的数据被覆盖,采用从前向后开始拷贝
在这里插入图片描述
当sorce<dest时,采用从后向前拷贝
memmove的优势就是可以进行自身的字符串拷贝

memcmp

int main()
{
    
    
	int arr1[] = {
    
    2,2,4,6,4};
	int arr2[] = {
    
    2,2,4,6,5};
	int ret = memcmp(arr1, arr2, 17);
	printf("%d", ret);
	return 0;
}

比较两个内存块大小,在vs环境下为小端存储,第17个字节以十六进制显示为arr1位04,arr2为05,所以打印-1

memset

int main()
{
    
    
	int arr1[] = {
    
     2,2,4,6,4 };
	int ret = memset(arr1,1,sizeof(arr1));
	int i = 0;
	for (i = 0; i < sizeof(arr1) / sizeof(arr1[0]); i++)
	{
    
    
		printf("%p ", arr1[i]);
	}
	return 0;
}

在这里插入图片描述
memset是将arr1数组的前sizeof(arr1)个字节的数据改为1
它是一个字节一个字节改的如上图

猜你喜欢

转载自blog.csdn.net/wan__xia/article/details/129693851