【C语言】字符串函数

目录

  • 1. strlen的使用和模拟实现
    • 1.1 strlen的使用
    • 1.2 strlen的模拟实现
      • 1.2.1 指针+整数实现
      • 1.2.2 指针-指针实现
      • 1.2.3 递归方式的实现
  • 2. strcpy的使用和模拟实现
    • 2.1 strcpy的使用
    • 2.2 strcpy的模拟实现
      • 2.2.1 版本1
      • 2.2.2 版本2
      • 2.2.3 版本3
      • 2.2.4 版本4
      • 2.2.5 版本5
  • 3. strcat的使用和模拟实现
    • 3.1 strcat的使用
    • 3.2 strcat的模拟实现
  • 4. strcmp的使用和模拟实现
    • 4.1 strcmp的使用
    • 4.2 strcmp的模拟实现
  • 5. strncpy函数的使用
  • 6. strncat函数的使用
  • 7. strncmp函数的使用
  • 8. strstr的使用和模拟实现
    • 8.1 strstr的使用
    • 8.2 strstr的模拟实现
  • 9. strtok函数的使用
  • 10. strerror函数的使用

1. strlen的使用和模拟实现

1.1 strlen的使用

【C语言】字符串函数_第1张图片
https://legacy.cplusplus.com/
我们使用上面这个网站,搜索strlen,就能了解strlen的使用。
上面说strlen是用来求字符串长度的,而它统计的是\0之前出现的字符的个数,也就是说字符串的结束标志是\0。

那举个列子来看一下:

int main()
{
char arr[] = "abcdef";
	//a b c d e f \0
	size_t len = strlen(arr);
	printf("%zd\n", len);
	return 0;
}

这里计算出的结果就是arr中字符串的长度,如下显示:

【C语言】字符串函数_第2张图片
但值得注意的是:
1.strlen函数要正确获得字符串长度的话,字符串中必须得有**\0**。
2. 要注意strlen的返回值类型是size_t

1.2 strlen的模拟实现

我们知道strlen是用来求字符串的长度的,那我们实现就只需要遍历字符串,统计一下出现在\0,之前的字符个数就行了。

1.2.1 指针+整数实现

使用count计数器,统计字符串的长度。
不过在计算字符串长度之前要先断言一下字符串是否为空assert(str != NULL)

#include 
#include 
#include 
size_t my_strlen(const char * str)
{
	size_t count = 0;
	assert(str != NULL);
    while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	char arr[] = "abc";
	size_t len = my_strlen(arr);
	printf("%zd\n", len);

	return 0;
}

很显然结果为3:
【C语言】字符串函数_第3张图片

1.2.2 指针-指针实现

我们在前面提到过指针-指针也就是地址-地址,得到的是指针和指针之间元素的个数。

#include 
#include 
#include 
size_t my_strlen(const char* str)
{
	const char* start = str;
	while (*str != '\0')
	{
		str++;
	}
	return str - start;
}
int main()
{
	char arr[] = "abc";
	size_t len = my_strlen(arr);
	printf("%zd\n", len);

	return 0;
}

很显然这里得到的结果与上面的相同也是3:
【C语言】字符串函数_第4张图片

1.2.3 递归方式的实现

当我们不使用使用临时变量时,怎么来模拟实现strlen函数呢?
此时就需要用到递归的思想:大事化小
当没有访问到\0时,我们每次判断是否访问到\0,就进行加1,然后继续调用my_strlen。
第一次1+my_strlen(“bc”)
第二次1+1+my_strlen(“c”)
第三次1+1+1+my_strlen(“”)
第四次就进不去,返回了0,最后1+1+1+0 = 3。

#include 
#include 
#include 
size_t my_strlen(const char* str)
{
	if (*str != '\0')
		return 1 + my_strlen(str + 1);
	else
		return 0;
}
int main()
{
	char arr[] = "abc";
	size_t len = my_strlen(arr);
	printf("%zd\n", len);

	return 0;
}

看下面结果还是3:
【C语言】字符串函数_第5张图片

2. strcpy的使用和模拟实现

2.1 strcpy的使用

【C语言】字符串函数_第6张图片
我们会发现在使用strcpy需要包含#include
strcpy函数的功能:拷贝字符串。

我们先来看一个示例:

#include 
#include 
#include 
int main()
{
	char arr1[] =   "hello abc";
	char arr2[4] = "xxx";
	strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

【C语言】字符串函数_第7张图片
我们发现此时代码报错了,这是为什么呢?
发现要将arr1中的拷贝到arr2中,发现arr2的空间根本不够存放arr1,此时才会报错。

所以我们得注意:

  1. 源字符串中必须包含\0,同时\0也会被拷贝到目标空间。
  2. 程序员自己要保证目标空间要足够大,能放得下拷贝来的数据。
  3. 保证目标空间必须可以修改。

我们再看一个例子:

#include 
#include 
#include 
int main()
{
	char arr1[4] = {'a', 'b', 'c', '\0'};
	char arr2[10] = "xxx";
	strcpy(arr2, arr1);
	printf("%s\n", arr2);
		return 0;
}

此时编译器就没有出现错误了
【C语言】字符串函数_第8张图片
那我们直接来模拟实现一下strcpy

2.2 strcpy的模拟实现

2.2.1 版本1

【C语言】字符串函数_第9张图片
要模拟实现strcpy,就需要两个指针来对其进行访问,arr1为源头指针src,arr2为目的指针dest。我们通过对arr1的遍历,将arr1中拷贝\0 前的字符都传给arr2。

#include 
#include 
#include 
void my_strcpy(char* dest, char* src)
{
	//拷贝\0	前的字符
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	//拷贝\0
	*dest = *src;
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

【C语言】字符串函数_第10张图片
那能优化一下实现方法吗?
那当然能了。

2.2.2 版本2

把后置++直接加到while()判断中

#include 
#include 
#include 
void my_strcpy(char* dest, char* src)
{
	while (*dest++ = *src++)
	{
		;
	}
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

出来的结果与上面的是一致的。
【C语言】字符串函数_第11张图片

那还能再修改吗?
当然。

2.2.3 版本3

这里加了对src和dest的断言。

void my_strcpy(char* dest, char* src)
{
	//NULL
	assert(dest);
	assert(src);
    while (*dest++ = *src++)
	{
		;
	}
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

【C语言】字符串函数_第12张图片

2.2.4 版本4

版本4: dest指向的空间是需要改变的,但是src指向的空间是不期望被给变的。

void my_strcpy(char* dest, const char* src)
{
	//NULL
	assert(dest);
	assert(src);

	while (*dest++ = *src++)
	{
		;
	}
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

结果显然也是一样的
【C语言】字符串函数_第13张图片

2.2.5 版本5

还有一种版本5:

char* my_strcpy(char* dest, const char* src)
{
	char* ret = dest;
	//NULL
	assert(dest);
	assert(src);

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

	return ret;//目标空间的起始地址返回
}

【C语言】字符串函数_第14张图片

3. strcat的使用和模拟实现

3.1 strcat的使用

【C语言】字符串函数_第15张图片
我们同样使用上面那个网站,我们发现strcat函数返回的是目标空间的起始地址。
【C语言】字符串函数_第16张图片
strcat 用于字符串追加,那既然是追加,我们先看一个例子。

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

}

将arr2中的字符串追加到arr1中:
【C语言】字符串函数_第17张图片
但是同样有需要注意的地方:

  1. 目标空间中得有\0(从哪里开始追加),源头字符串中得有\0(追加到什么时候结束)。
  2. 目标空间要足够大,目标要可以修改。

这对于我们自己用代码实现strcat的功能是很重要的。

3.2 strcat的模拟实现

要模拟实现strcat就要找到找到目标空间中的\0,然后再进行拷贝。
通过while循环遍历找到找到目标空间中的\0,然后将源空间中的赋值到目标空间。
下面我们用代码实现一下

char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);
	while (*dest != '\0')
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

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

	return 0;
}

结果与使用函数所得到的结果是一致的
【C语言】字符串函数_第18张图片

4. strcmp的使用和模拟实现

4.1 strcmp的使用

【C语言】字符串函数_第19张图片
我们同样使用上面的网站,搜索strcmp,那一起来看看怎么使用它。
strcmp是用于比较两个字符串的,不过它比较的是两个两个字符串中对应位置上的字符,而且是按字典序比较。

【C语言】字符串函数_第20张图片
像这样对应的相比较,如果两个有相同字符,那就继续往后比较。
有三种情况,像上图那种,字符串2中q比字符串1中c大,返回的就是一个小于0的数字。
第二种,字符串2比字符串小,返回的就是一个大于0的数字。
第三种,字符串2和字符串相等,返回的就是0。

实现一下上面的代码来看看结果

#include 
#include 

int main()
{
	int ret = strcmp("abcdef", "abq");
	if (ret > 0)
		printf(">\n");
	else if (ret == 0)
		printf("==\n");
	else
		printf("<\n");
	printf("%d\n", ret);

	return 0;
}

【C语言】字符串函数_第21张图片
结果和分析的是一样的。
但这里它返回的值一定是-1吗?
并不是,我们所说的是小于0的数字都可以。

4.2 strcmp的模拟实现

那我们自己来模拟实现一下strcmp
首先传的两个字符串不能为空,再逐一遍历两个字符串相同位置的字符进行比较。
要注意的是,我们不改变这两个字符串的内容,所以我们加了const修饰(const char* s1, const char* s2)
当两个字符串里面的字符有不同的时候结束循环,再来判断里面字符的字典序号。

#include 
#include 
#include 
int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 != NULL);
	assert(s2 != NULL);
    while (*s1 == *s2)
	{
		if (*s1 == '\0')
			return 0;

		s1++;
		s2++;
	}
	if (*s1 > *s2)
		return 1;
	else
		return -1;
}
int main()
{
	int ret = my_strcmp("abcdef", "abq");
	if (ret < 0)
		printf("<\n");

	return 0;
}

我们看到结果与上面的是一致的。
【C语言】字符串函数_第22张图片

5. strncpy函数的使用

【C语言】字符串函数_第23张图片
strncpystrcpy对比发现,strncpy多了传入的数量,长度受到了限制

【C语言】字符串函数_第24张图片
我们来看一下例子

#include 
#include 
int main()
{
	char arr1[10] = "xxxxxxxxx";
	char arr2[] = "abc";
	strncpy(arr1, arr2, 5);
	printf("%s\n", arr1);

	return 0;
}

将arr2中的5个字节传给arr1,这里的5是以字节为单位。
【C语言】字符串函数_第25张图片

6. strncat函数的使用

【C语言】字符串函数_第26张图片
【C语言】字符串函数_第27张图片
和上面的一样,也就是长度受到了限制。
来看一个例子

#include 
#include 
int main()
{
	char arr1[20] = "abc";
	char arr2[] = "def";
	strncat(arr1, arr2, 5);
	printf("%s\n", arr1);

	return 0;
}

在arr1后面追加了arr2的5个字节:
【C语言】字符串函数_第28张图片

7. strncmp函数的使用

【C语言】字符串函数_第29张图片
【C语言】字符串函数_第30张图片
同样的道理,也是多了长度受到了限制。
那举个例子看看:

#include 
#include 
int main()
{
	char arr1[20] = "abcdef";
	char arr2[] = "abcqwer";
	int ret = strncmp(arr1, arr2, 4);

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

	return 0;
}

【C语言】字符串函数_第31张图片
比较两个字符串中的4个字节,发现q比c的字典序大,返回一个小于0的数

8. strstr的使用和模拟实现

8.1 strstr的使用

【C语言】字符串函数_第32张图片
这个函数是用来干什么的呢?
strstr函数的功能:如果str2在str1中出现,那就返回第一次出现的位置;
如果没有出现,就返回NULL
【C语言】字符串函数_第33张图片
就图中而言返回的位置就是str1的d的位置,所以我们来看看相关代码结果如何。

#include 
#include 
int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "def";
	/*char* ret = my_strstr(arr1, arr2);*/
	char* ret = strstr(arr1, arr2);
	if (ret != NULL)
		printf("%s\n", ret);
	else
		printf("找不到\n");

	return 0;
}

结果很显然如同所想的一样。
【C语言】字符串函数_第34张图片

8.2 strstr的模拟实现

str2如果子串是空字符串,直接返回str1。
要先保证str1和str2不为空。
因为找不到在匹配的字符串要返回起始地址,所以不在原有的str1和str2上进行,而使用cp先记录下 str1。用s1和s2来继续匹配,当s2遍历完str2时,并没有发现有与str1中相匹配时,就重新返回str2,方便下一次的匹配。

来用代码实现一下

#include 
#include 
const char* my_strstr(const char* str1, const char * str2)
{
	assert(str1);
	assert(str2);

	const char* cp = str1;
	const char* s1 = NULL;
	const char* s2 = NULL;

	//如果子串是空字符串,直接返回str1
	if (*str2 == '\0')
		return str1;

	while (*cp)
	{
		s1 = cp;
		s2 = str2;
		while (*s1 == *s2 && *s1 && *s2)
		{
			s1++;
			s2++;
		}

		if (*s2 == '\0')
			return cp;
    	cp++;
	}

	return NULL;
}

int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "def";
	char* ret = my_strstr(arr1, arr2);
	if (ret != NULL)
		printf("%s\n", ret);
	else
		printf("找不到\n");

	return 0;
}

很显然结果与我们上面用函数实现的一样:
【C语言】字符串函数_第35张图片

9. strtok函数的使用

【C语言】字符串函数_第36张图片strtok函数其实并不常用,那它又有什么作用呢?
其实strtok函数就是将给定一个分割符,以分割符为界,将其他部分分开。
那它遇到分割符之后怎么做呢?
在图片的方法中介绍的它把分割符置为NULL,然后又继续往后查找。

【C语言】字符串函数_第37张图片

我们看一下它的代码:

#include 
#include 
int main()
{
	char arr[] = "192.168.101.25";//. 分割符
	char buf[60] = { 0 };
	strcpy(buf, arr);
	char* p = ".";

	char* r = NULL;
	for (r = strtok(buf, p); r != NULL; r = strtok(NULL, p))
	{
		printf("%s\n", r);
	}
	
	return 0;
}

结果显然与分析的一致
【C语言】字符串函数_第38张图片

10. strerror函数的使用

【C语言】字符串函数_第39张图片
要学习strerror函数,就得先了解errno

  1. 当库函数调用失败的时候,会讲错误码记录到errno这个变量中
  2. errno是一个C语言的全局变量
    错误码就是编译器记录的,每一个数字对应不同的错误。
    那我们来看看0到9对应的错误是什么。
    代码走一下看看:
#include 
#include 
#include 
int main()
{
	for (int i = 0; i < 10; i++)
	{
		char* ret = strerror(i);
		printf("%d  :  %s\n", i, ret);
	}
	return 0;
}

【C语言】字符串函数_第40张图片

perrorstrerror同样都是返回错误的
举个例子看看perrorstrerror
打开文件 ,在读写文件之前,需要打开文件。
如果要打开成功,需要文件是存在的,如果文件不存在,则打开失败,fopen会返回NULL

#include 
#include 
#include 
int main()
{
	FILE* pf = fopen("add.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		printf("fopen: %s\n", strerror(errno));
		return 1;
	}
	else
	{
		printf("打开文件成功\n");
		//...
	}
	return 0;
}

它们给出的都是文件不存在:

【C语言】字符串函数_第41张图片
perrorstrerror不同的地方在于
perror会直接在屏幕是输出错误的地方,而strerror则不会,如果需要在屏幕上输出就要用到printf

有不足的地方请多多指正,让我们共同进步。

你可能感兴趣的:(C语言,c语言,算法,数据结构)