c语言 字符串函数和内存函数

一.字符串函数

      什么是字符串函数?从字面上理解,就是对字符串函数,那么有哪些字符串函数呢?

1.strlen

  通过前面知识的学习,我们知道我们的老朋友strlen是求字符串长度。这是一个很有个性的字符串函数,他只数\0之前的字符个数

头文件:string.h

返回值是:size_t  -->  无符号

模拟实现:原理是对字符串数到\0我们就停了不再计数

方法1:计数器

//strlen的模拟实现
//1.循环遍历字符串
size_t mystrlen_1(const char *arr)
{
	size_t count = 0;
	while (*arr!='\0')
	{
		count++;
		arr++;
	}
	return count;

}

int main1()
{
	char arr[] = "abcdef";
	size_t ret = mystrlen_1(arr);
	printf("字符串的长度为%zd", ret);
	return 0;
}

方法2:指针-指针=元素个数

size_t mystrlen_2(char* arr)
{
	char* tmp = arr;
	while (*arr != '\0')
	{
		arr++;

	}
	return arr - tmp;

}
int main2()
{
	char arr[] = "abcdef";
	size_t ret = mystrlen_2(arr);
	printf("字符串的长度为%zd", ret);
	return 0;
}

方法3:递归思想 

/法3递归思想
//1+mystrlen_3(arr+1)
//1+1+mystrlen_3(arr+1)
//....
size_t mystrlen_3(char* arr)
{
	if (*arr == '\0')
	{
		return  0;
	}
	else
	{
		return 1 + mystrlen_3(arr + 1);
	}
}
int main3()
{
	char arr[] = "abcdef";
	size_t ret = mystrlen_3(arr);
	printf("字符串的长度为%zd", ret);
	return 0;
}

2.一些不怎么安全的字符函数 strcpy strcat strcmp

strcpy:这个函数就是字符串的拷贝。

 要求:源字符串必须以'\0'结尾 / 目标空间必须足够大 /目标空间可修改(常量字符串不可修改

模拟实现:原理是运用双指针将源字符串赋给目标字符串

void mystrcpy_1(char* arr1, char* arr2)
{
	assert(arr1&&arr2);
	while (*arr2 != '\0')
	{
		*arr1 = *arr2;
		arr1++;
		arr2++;
	}
	*arr1 = *arr2;//\0也要copy
}
void mystrcpy_2(char* arr1, char* arr2)
{
	assert(arr1 && arr2);
	while (*arr1++=*arr2++)
	{
		;
	}

}
char* mystrcpy_3(char*arr1, char*arr2)
{
	char* ret = arr1;
	assert(arr1 && arr2);
	while (*arr1++ = *arr2++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "abcdef";
	mystrcpy_2(arr1, arr2);
	printf("%s\n", arr1);
	printf("%s\n", mystrcpy_3(arr1, arr2));

	return 0;
}

strcat:这个函数是用来实现对字符串的追加

要求:源字符串必须以'\0'结束 / 目标空间要足够大且可以修改 / 最好不要自己给自己追加可能发生死循环

模拟实现:原理是先找出目标的‘\0’将其覆盖,再拷贝

strcat的使用及实现
///*char**/void  mystrcat_(char* arr1, char* arr2)
//{
//	char * ret = arr1;
//	assert(arr1 && arr2);
//	//1.找到\0
//	while (*arr1)
//	{
//		arr1++;
//	}
//	//2.拷贝
//	while (*arr1++ = *arr2++)
//	{
//		;
//	}
//	/*return ret;*/
//
// }
//
//int main()
//{
//	//目标要足够大
//	char arr1[20] = "hello ";
//	char arr2[] = "world";
//	mystrcat_(arr1, arr2);
//	printf("%s\n",arr1);
//	return 0;
//}

strcmp:这个函数是用来比较字符串

特点:比较对应位置的字符ascii码。例如“abcd"和”abcq",前面的都相同,d和q不同,d的ASCII码小于q的,所以小于

返回类型是:size_t

模拟实现:原理是一个一个比较,找到不同的位置,再进行比较。这里的优化可以用指针-指针,因为小于返回的是负数,大于返回正数

int mystrcmp_1(const char*s1,const char* s2)
{
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
		{
			return 0; //相同情况
		}
		s1++;
		s2++;
	}
	if (*s1 > *s2)
	{
		return 1;
	}
	else
	{
		return -1;
	}

}
int mystrcmp_2(const char* s1, const char* s2)
{
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
		{
			return 0; //相同情况
		}
		s1++;
		s2++;
	}
	return *s1 - *s2;//一样的 大于返回正数 小于返回负数 以ASCII码值计算

}
//一个一个位置比较
int main2()
{
	char arr1[] = "bbq";
	char arr2[] = "abcd";
	int ret = mystrcmp_2(arr1, arr2);
	if (ret > 0)
	{
		printf("大于\n");
	}
	else if (ret < 0)
	{
		printf("小于\n");
	}
	else
	{
		printf("等于\n");
	}
	return 0;
}

3.一些相对安全的字符串函数 strncpy strncat

strncpy:这个函数在strcpy的基础上有copy个数限制

特点:当长度不够时,会用‘\0'来补充。你要拷贝几个就几个。

模拟实现:

/模拟实现strncpy
char* mystrncpy(char* dest, char* src, size_t num)
{
	char* tmp = dest;
	assert(dest && src); 
	size_t len = strlen(src);
	size_t count = num ;
	while (count--)
	{
		if (count > len)
		{
			*(src + count) = '\0';
		}
		*(dest+count) = *(src + count);

	}
	return tmp;
}
int main5()
{
	char arr1[]   = "hello";
	//char arr2[20] = "bitxxxxx";
	char arr2[20] = { 0 };
	size_t n = 0;
	scanf("%zd", &n);
	printf("%s\n", mystrncpy(arr2, arr1, n));
	return 0;
}

strncat:这个函数是在strcat的基础上进行追加个数的限制

特点:当追加长度小于源字符串,他不会给你多加。每次追加完都会给你带个\0

模拟实现:

//模拟实现strncat 特点是你要追加几个就几个,搞完给你来个\0,之后没用\0补
char* mystrncat(char* dest, char* src, size_t n)
{
	size_t count = n;
	char* tmp = dest;
	assert(dest && src);
	//1.找目标\0
	while (*dest)
	{
		dest++;
	}
	//2.拷贝
	while (*dest++ = *src++ && count--)
	{
		;
	}
	return tmp;


}
int main()
{
	char arr1[20] = "hello";
	char arr2[20] = "world";
	size_t n = 0;
	scanf("%zd",&n);
	printf("%s\n", mystrncat(arr1, arr2, n));
	return 0;
}

strncmp:比较arr1和arr2的前n个字符

4.奇怪的字符串函数 strstr strtok strerror

strstr:这个函数是用来从目标字符串找源字符串的。

特点:函数返回的是源字符串在目标字符串第一次出现的位置/匹配不包括\0,以\0为结束标志

模拟实现:运用双指针和一个临时变量来记录位置,一个地方为起始位置不匹配就移动这个临时变量来更新起始位置。

//模拟实现strstr这个字符串函数的功能是从目标字符窜找出源字符串的位置
char* mystrstr_(char* dest,char*src)
{   
	assert(dest && src);
	if (src == '\0')
	{
		return NULL;
	}
	char* cur = dest;
	char* s1 = NULL;
	char* s2 = NULL;
	while (*cur)
	{
		//让cur作为记录位置的指针
		//abbbcdef
		//bbc
		s1 = cur;
		s2 = src;
		while (*s1 == *s2)
		{
			s1++;
			s2++;
		}
		//最后如果找到,就会加到\0
		if (*s2 == '\0')
		{
			return cur;
		}
		cur++;//不匹配移动 
	}
	return NULL;
}
int main1()
{
	char arr1[20] = "abbbcdef";
	char arr2[] = "bbc";
	printf("%s\n", mystrstr_(arr1, arr2));
	return 0;
}

strtok:这个函数用来分割字符串

特点:

1 char * strtok ( char * str, const char * sep);
sep参数指向⼀个字符串,定义了⽤作分隔符的字符集合
第⼀个参数指定⼀个字符串,它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标
记。
strtok函数找到str中的下⼀个标记,并将其⽤ \0 结尾,返回⼀个指向这个标记的指针。(注:
strtok函数会改变被操作的字符串,所以在使⽤strtok函数切分的字符串⼀般都是临时拷⻉的内容
并且可修改。)
应用:
int main4()
{
	char arr[] = "[email protected]";
	char* p = "@.";
	char* s = NULL;
	for (s = strtok(arr, p);s!=NULL;s=strtok(NULL,p))
	{
		printf("%s\n", s);
	}
	return 0;
}

strerror: strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。

    错误码⼀般是放在 errno.h 这个头⽂件中说明 的,C语⾔程序启动的时候就会使⽤⼀个全⾯的变量errno来记录程序的当前错误码,只不过程序启动 的时候errno是0,表⽰没有错误,当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会讲对应 的错误码,存放在errno中,⽽⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。

应用:

//strerror perror
#include
int main9()
{
	int i = 0;
	for (i = 0; i <= 100; i++)
	{

		printf("%d:%s\n", i, strerror(i));
	}


	return 0;
}

int main()
{
	FILE* pfile;
	pfile = fopen("unexist.ent","r");
	if (pfile == NULL)
	{
		printf("Error openning file unexist.ent:%s\n", strerror(errno));
	}
	return 0;
}

二.内存函数

1.memcpy

memcpy函数操作单位是字节,通过内存进行copy

特点:遇到\0不会停下继续拷贝,内存重叠和不重叠都可以实现copy

模拟实现:此代码无法实现内存重叠 原理是以1个字节为单位交换

//memcpy的实现
void* mymemcpy_(void* dest, void* src, size_t num)
{
	assert(dest && src);
	void* tmp = dest;
	while (num--)
	{
		//char为1个字节,比较简便
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return tmp;

}


int main2()
{//以内存来copy
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 2,3,4,5,6};
	mymemcpy_(arr1, arr2, 5 * sizeof(int));
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

memmove:专门用来处理内存重叠

模拟实现:对dest在src前,采用从前往后copy;对dest在src后,采用从后往前copy

//memmove 内存重叠
//1 2 3 4 5 6 7 8 9 10
//dest在src前采用前-后打印 
//dest在src后采用后-前打印
void* mymemmove_(void*dest,void*src,size_t num)
{
	void* tmp = dest;
	if (dest < src)
	{
		//前-后
		while (num--)
		{
			//char为1个字节,比较简便
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}

	}
	else
	{
		//后-前
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
			//num--一直往后推进
		}
	}
	return tmp;
}
int main3()
{
	int arr1[10]={1,2,3,4,5,6,7,8,9,10};
	int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };
	mymemmove_(arr1 , arr1+3, 5 * sizeof(int));//destsrc

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);

	}
	printf("\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}

	return 0;
}

分享到此结束啦,感谢各位!  

你可能感兴趣的:(c语言,开发语言)