字符函数和字符串函数

目录

目录

1.求字符串函数

strlen

 第1,2点的小坑,观察下面的代码:

 第3点小坑(关于strlen的返回值),观察下面的代码:

模拟实现strlen(3种方法)

2.长度不受限制的字符串函数

strcpy

第1点

 第2点: 

 第3点:

 第4点:

第5点:

模拟实现strcpy

strcat

第1点:调用演示

第2,3,4点参考strcpy

第5点:字符串无法自己给自己追加

模拟实现strcat

strcmp

第1点:

 模拟实现strcmp

长度受限制的字符串

strncpy

 用法:

​编辑 第3点:

 模拟实现strncpy

strncat

第1点:  追加字符串时,strncat末尾会补\0代码:

模拟实现strncat

strncmp

 字符串查找:

strstr

用法:

 模拟实现strstr

​编辑

 strtok

 用法演示:

 字符操作

 错误信息报告

strerror

用法:

 内存操作函数

memcpy

 模拟实现memcpy

 memmove

模拟实现memmove 

memcmp 

 memset

 一个小坑:

这篇文章的大纲:

字符函数和字符串函数_第1张图片

 我会按照图片讲解和模拟实现部分函数

1.求字符串函数

strlen

作用 :求字符串长度(不包含\0)

注意点:

 第1,2点的小坑,观察下面的代码:

#include
#include

int main()
{
	char arr1[] = { 'b', 'i', 't' };
	char arr2[] = { 'b', 'i', 't','\0'};
	char arr3[] = "bit";

	int len1 = strlen(arr1);
	int len2 = strlen(arr2);
	int len3 = strlen(arr3);

	printf("%d\n", len1);
	printf("%d\n", len2);
	printf("%d\n", len3);

	return 0;
}

 输出结果:

字符函数和字符串函数_第2张图片

 同样求"bit"的长度,三个结果不同的原因:

arr1,arr2原本是字符数组,arr2在arr1的基础上加上'\0'可以当成字符串,用strlen计算字符串长度时直接计算'\0'之前出现的元素个数即可;arr3本身就是字符串,所以arr2和arr3结果相同都是3。arr1后并没有跟'\0'依然是字符数组,它在内存中的存储:

字符函数和字符串函数_第3张图片

用strlen来求取长度时,strlen要去寻找'\0', 但它在内存中前后位置的元素是不可知的,所以什么时候找到'\0'也是未知的,所以strlen求出的长度是随机值。(在不同的机器上可能结果不同)

 第3点小坑(关于strlen的返回值),观察下面的代码:

int main()
{
	if (strlen("abc") - strlen("abcdef") > 0)
	{
		printf(">\n");     
	}
	else
	{
		printf("<\n");
	}
	return 0;
}

输出结果:

第一眼看觉得输出结果毋庸置疑是"<",其实这就是很多新手没有理解strlen的返回值,strlen返回值是size_t,size_t是由type_def重定义unsigned int,所以size_t其实是无符号整型,计算strlen("abc") - strlen("abcdef")的结果是-3,-3以无符号整型出现时会被当成一个很大的正数,所以两个相减的结果>0会输出">"

模拟实现strlen(3种方法)

首先观察这个函数的返回值和参数:

字符函数和字符串函数_第4张图片

方法1: 计数器实现


size_t my_strlen(const char* str)
{
	assert(str);
	int count = 0;

	while (*str != '\0')
	{
		count++;
		str++;
	}

	return count;
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);

	printf("%d\n", len);
	return 0;
}

 方法2:递归实现

size_t my_strlen(const char* str)
{
	assert(str);
	
	if(*str != '\0')
	{
		return 1 + my_strlen(str + 1);
	}
	else
	{
		return 0;
	}
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);

	printf("%d\n", len);
	return 0;
}

关于法1,法2具体介绍:

方法3: 指针-指针实现

size_t my_strlen(const char* str)
{
	assert(str);
	const char* ptr = str;

	while (*ptr != '\0')
	{
		ptr++;
	}
	return (ptr-str);
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);

	printf("%d\n", len);
	return 0;
}

简单介绍:指针-指针返回的是2个指针之间的元素个数,所以用一个指针记录首元素的位置,另一个指针通过循环找到'\0'之前那个元素的位置,二者相减即可得到字符串的长度

2.长度不受限制的字符串函数

strcpy

作用:拷贝字符串

注意点:

字符函数和字符串函数_第5张图片

第1点

 可能新手是以赋值的方式来拷贝字符串的,那就错了,观察下面的代码:

int main()
{
	char name[30] = { 0 };
	//"zhangsan"
	//string cpoy

	//name = "zhangsan";  //err,  name数组名是地址,不是空间,地址是一个常量值,不能被赋值

	strcpy(name, "zhangsan");

	printf("%s\n", name);

	return 0;
}

输出结果:

 以上注释内容就是错误拷贝方法,错误原因见注释,正确拷贝字符串调用strcpy,strcpy函数用法:

将source的内容拷贝到destination中

字符函数和字符串函数_第6张图片

 第2点: 

错误示例:

int main()
{

	char name[30] = "xxxxxxx";

	char arr[] = { 'b','i','t'};

	strcpy(name, arr);

	printf("%s\n", name);         

	return 0;
}

输出结果:

 bit后没有跟'\0',什么时候出现'\0'是未知的,所以就将'\0'之前位置的未知元素也拷贝过来了

正确写法:

int main()
{
	char name[30] = "xxxxxxx";

	char arr[] = { 'b','i','t', '\0'};

	strcpy(name, arr);

	printf("%s\n", name);         

	return 0;
}

输出结果:

 第3点:

int main()
{

	char name[30] = "xxxxxxx";

	strcpy(name, "zhang\0san");

	printf("%s\n", name);         //打印出来zhang

	return 0;
}

输出结果:

 调试观察:

字符函数和字符串函数_第7张图片

 字符函数和字符串函数_第8张图片

 第4点:

当目标空间小于源字符串长度时,程序会出现崩溃,下面的代码就出现了程序崩溃

int main()
{
	char name[3] ={0};

	char arr[] = "abcdef";

	strcpy(name, arr);

	printf("%s\n", name);

	return 0;
}

第5点:

当目标空间是常量字符串时,是不可以拷贝的,观察下面代码:

int main()
{
	const char* p = "abcdef";    //常量字符串,不可修改

	char arr[] = "bit";

	strcpy(p, arr);        //目标区域不可修改

	return 0;
}

模拟实现strcpy

字符函数和字符串函数_第9张图片

char* my_strcpy(char*dest,const char* src)
{
	assert(dest && src);

	const char* ret = src;

	while (*dest++ = *src++)
	{
		;
	}

	return ret;
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };

	my_strcpy(arr2,arr1);

	printf("%s\n", arr2);

	return 0;
}

简单介绍:就是通过循环将源字符串的元素一一拷贝到目标空间中,直至遇到'\0'为止,最后返回源字符串首元素的地址

strcat

作用:字符串追加

注意点:

字符函数和字符串函数_第10张图片

第1点:调用演示

调用: 将源字符串追加到目标空间之后

 代码:

int main()
{
	char arr1[20] = "hello ";

	strcat(arr1, "world");

	printf("%s\n", arr1);

	return 0;
}

输出结果: 

第2,3,4点参考strcpy

第5点:字符串无法自己给自己追加

原因:正如下面模拟实现strcat一样,如果目标空间和源字符串都是同一个字符串时,那么dest++找到'\0'时,src的内容也改变了,无法自己给自己追加

错误示例:

 char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);

	char* ret = dest;

	while (*dest != '\0')
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
		;
	}

	return ret;
}
int main()
{
	char arr2[20] = "hello ";

	my_strcat(arr2, arr2);        

	printf("%s\n", arr2);

	return 0;
}

输出结果:

模拟实现strcat

字符函数和字符串函数_第11张图片

思路:分2步, 1.  找到目标字符串的末尾   2.  将源字符串拷贝到目标字符串末尾的后面

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);

	char* ret = dest;

	//1. 找到目标空间空间的末尾
	while (*dest != '\0')
	{
		dest++;
	}

	//2. 拷贝字符串
	while(*dest++ = *src++)
	{
		;
	}

	return dest;
}
int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";

	my_strcat(arr1, arr2);

	printf("%s\n", arr1);

	return 0;
}

strcmp

作用:比较字符串

注意点:

字符函数和字符串函数_第12张图片

两个字符串是如何比较大小的:

字符函数和字符串函数_第13张图片

第1点:

新手可能会犯的错误,如下:

int main()
{
	char arr1[20] = "zhangsan";
	char arr2[] = "zhangsanfeng";

	//比较两个字符串是否相等
	//arr1是数组名,数组名是数组首元素的地址
	//arr2是数组名,数组名是数组首元素的地址

	//这里是在比较2个地址,而不是2个字符串的内容,2个字符串的地址是不相同的

	if (arr1 == arr2)
	{
		printf("==\n");
	}
	else
	{
		printf("!=\n");
	}

	return 0;
}

输出结果: 解释见代码中的注释

 正确比较字符串的方法,如下:

int main()
{
	char arr1[] = "abcdp";
	char arr2[] = "abcw";

	int ret = strcmp(arr1, arr2);

	if (ret < 0)
	{
		printf("<\n");
	}
	else if (ret == 0)
	{
		printf("=\n");
	}
	else
	{
		printf(">\n");
	}

	return 0;
}

输出结果:

 模拟实现strcmp

字符函数和字符串函数_第14张图片

简单思路:就是通过循环让str1和str2中的元素一一去比较,一旦相等继续比较,一旦不相等返回

(*str1 - *str2)的值,中间的优化是一旦在循环中某个字符串遇到'\0'就return 0,表示两个字符串相等

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);

	while (*str1 == *str2)
	{
		if (*str1 == '\0')
		{
			return 0;            //相等
		}
		str1++;
		str2++;
	}

	return (*str1 - *str2);
}
int main()
{
	char arr1[] = "abc";
	char arr2[] = "abcq";
	
	int ret = my_strcmp(arr1, arr2);

	if (ret < 0)
	{
		printf("<\n");
	}
	else if (ret == 0)
	{
		printf("=\n");
	}
	else
	{
		printf(">\n");
	}

	return 0;
}

长度受限制的字符串

strncpy

注意点:

字符函数和字符串函数_第15张图片

 用法:

字符函数和字符串函数_第16张图片 第3点:

观察代码:

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "bit";

	strncpy(arr1, arr2, 5);

	printf("%s\n", arr1);

	return 0;
}

输出结果:

 调试观察:

字符函数和字符串函数_第17张图片

字符函数和字符串函数_第18张图片

 模拟实现strncpy

char*my_strncpy(char* dest, const char* src, size_t num)
{
	assert(dest && src);
	char* ret = src;

	while (num--)
	{
		*dest++ = *src++;
	}

	*dest = '\0';
	return ret;
}
int main()
{
	char arr1[] = "abcdefgh";
	char arr2[] = "hello";

	my_strncpy(arr1, arr2, 5);

	printf("%s\n", arr1);

	return 0;
}

strncat

注意点:

 用法:

int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";

	strncat(arr1, arr2, 3);

	printf("%s\n", arr1);

	return 0;
}

输出结果:

第1点:  追加字符串时,strncat末尾会补\0
代码:

int main()
{
	char arr[20] = "hello\0xxxx";
	char arr2[] = "bit";

	strncat(arr, arr2, 3);

	printf("%s\n", arr);

	return 0;
}

输出结果:

 调试观察: 

字符函数和字符串函数_第19张图片

 字符函数和字符串函数_第20张图片

模拟实现strncat

char* my_strncat(char* dest, const char* src, size_t num)
{
	assert(dest && src);

	char* ret = dest;

	while (*dest != '\0')
	{
		dest++;
	}

	while (num--)
	{
		*dest++ = *src++;
	}

	return dest;
}
int main()
{
	char arr1[15] = "hello ";
	char arr2[] = "world";

	my_strncat(arr1, arr2, 5);

	printf("%s\n", arr1);

	return 0;
}

strncmp

注意点: 

字符函数和字符串函数_第21张图片

用法如下:

字符函数和字符串函数_第22张图片

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcq";

	int ret = strncmp(arr1, arr2, 3);

	printf("%d\n", ret);

	if (ret < 0)
	{
		printf("<\n");
	}
	else if (ret == 0)
	{
		printf("=\n");
	}
	else
	{
		printf(">\n");
	}
    return 0;
}

输出结果:

字符函数和字符串函数_第23张图片

 字符串查找:

strstr

作用:查找子串

用法:

int main()
{
	char email[] = "[email protected]";
	char substr[] = "bitejiuyeke";
	char* ret = strstr(email, substr);

	if (ret == NULL)
	{
		printf("子串不存在\n");
	}
	else
	{
		printf("%s\n", ret);
	}

	return 0;
}

输出结果:

字符函数和字符串函数_第24张图片

 模拟实现strstr

字符函数和字符串函数_第25张图片

思路讲解:

字符函数和字符串函数_第26张图片

字符函数和字符串函数_第27张图片

 代码:

char* my_strstr(char* str1, char* str2)
{
	assert(str1 && str2);

	char* s1 = str1;
	char* s2 = str2;
	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++;
	}

	return NULL;
}
int main()
{
	char email[] = "[email protected]";
	char substr[] = "bitejiuyeke";
	char* ret = my_strstr(email, substr);

	if (ret == NULL)
	{
		printf("子串不存在\n");
	}
	else
	{
		printf("%s\n", ret);
	}

	return 0;
}

 strtok

作用:切割字符串

注意点:

字符函数和字符串函数_第28张图片

字符函数和字符串函数_第29张图片

 用法演示:

麻烦写法:

int main()
{
	const char* sep = "@.";
	char arr1[] = "[email protected]";
	char arr2[50] = { 0 };

	strcpy(arr2, arr1);

	char*ret=strtok(arr2, sep);
	if(ret != NULL)
	printf("%s\n", ret);

     ret = strtok(NULL, sep);
	 if (ret != NULL)
	printf("%s\n", ret);

    ret = strtok(NULL, sep);
	if (ret != NULL)
	printf("%s\n", ret);

	ret = strtok(NULL, sep);
	if (ret != NULL)
	printf("%s\n", ret);

	return 0;
}

简洁写法

int main()
{
	char* sep = "@.";
	char arr1[] = "[email protected]";
	char arr2[50] = { 0 };

	strcpy(arr2, arr1);

	char* ret = NULL;

	for (ret = strtok(arr2, sep); ret != NULL; ret = strtok(NULL, sep))
	{
		printf("%s\n", ret);
	}
;
	return 0;
}

输出结果:

字符函数和字符串函数_第30张图片

 字符操作

字符函数和字符串函数_第31张图片

 错误信息报告

strerror

作用:返回错误码所对应的错误信息

用法:

int main()
{

	//errno - C语言设置的一个全局的错误码存放的变量

	FILE* pf = fopen("C:\\Users\\86189\\Desktop\\test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	else
	{
		//
	}
	return 0;

}

输出:

 内存操作函数

memcpy

作用:负责拷贝两块独立空间中的数据

区别于strcpy,strcpy是拷贝字符串的

 注意点:

字符函数和字符串函数_第32张图片

调用演示:

int main()
{
	int arr1[] = {0,1,2,3,4,5,6,7,8,9};
	int arr2[10] = { 0 };

	memcpy(arr2, arr1, 40);

	float arr3[5] = { 1.0, 2.5,3.0,5.0,6.0 };
    float arr4[10] = { 0.0 };
    memcpy(arr4, arr3, 20);

	return 0;
}

调试观察:

字符函数和字符串函数_第33张图片

 字符函数和字符串函数_第34张图片

 模拟实现memcpy

字符函数和字符串函数_第35张图片

简单思路:

通过循环将src中的元素拷贝到dest中,因为memcpy拷贝时是不限制数据类型的,所以要用void*的指针作为形式参数,赋值时也不能直接赋值首先要将void*的指针强制类型转换成char*在赋值,用char*指针的原因是char*指针每次访问一个字节,而如果用其他类型的指针如int,每次访问4个字节,想拷贝19个字节是没有办法的;而对void*指针是没法直接++的,要先强制类型转换成char*再++

代码:

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assetr(dest && src);

	const void* ret = src;

	while (num--)
	{
		*(char*)dest = *(char*)src;

		//err:
		//dest++;
		//src++;

		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}
int main()
{
	int arr1[] = {0,1,2,3,4,5,6,7,8,9};
	int arr2[10] = { 0 };

	my_memcpy(arr2, arr1, 40);

	float arr3[5] = { 1.0, 2.5,3.0,5.0,6.0 };
    float arr4[10] = { 0.0 };
    my_memcpy(arr4, arr3, 20);

	return 0;
}

 memmove

作用: 负责拷贝重叠内存之间的数据

注意点:

字符函数和字符串函数_第36张图片

 调用演示:

int main()
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };

	memmove(arr, arr + 2, 12);
	//2 3 4 3 4 5 6 7 8 9

	int i = 0;

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

	return 0;
}

输出结果:

模拟实现memmove 

字符函数和字符串函数_第37张图片

思路讲解: 

字符函数和字符串函数_第38张图片

代码:

void* my_memmove(void* dest, const void* src, size_t num)
{
	assert(dest && src);

	void* ret = src;

	if(dest 后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
		    src = (char*)src + 1;
		}
	}
	else
	{
		//后 -> 前
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}

	return ret;
}
int main()
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };

	my_memmove(arr, arr + 2, 12);
	//2 3 4 3 4 5 6 7 8 9

	int i = 0;

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

	return 0;
}

memcmp 

作用:比较内存中的数据

注意点:

字符函数和字符串函数_第39张图片

 调用演示:

int main()
{
	int arr1[] = { 1,2,3,4};   //01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
	int arr2[] = { 1,3,2};     //01 00 00 00 03 00 00 00 02 00 00 00

	int ret = memcmp(arr1, arr2, 12);
	printf("%d\n", ret);

	return 0;
}

输出结果:

 memset

作用:内存设置

 调用演示:

int main()
{
	char arr[] = "hello bit";

	memset(arr+6, 'x', 3);

	printf("%s\n", arr);

	return 0;
}

输出结果:

 一个小坑:

需要将一个数组全部初识化为1时,观察下面代码:

int main()
{
	//把arr初始化为全1

	int arr[10] = { 0 };
	memset(arr, 1, 40);    //一个字节一个字节初始化
	int i = 0;

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

	return 0;
}

输出结果:

字符函数和字符串函数_第40张图片

原因分析:

每个字节都设置为1

字符函数和字符串函数_第41张图片

字符函数和字符串函数_第42张图片

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