C语言十大字符串函数——掌握大半个<string.h>

1. strlen——求字符串长度

1.1 strlen用法

#include <stdio.h>
#include <string.h>//使用strlen函数必要的头文件
int main()
{
    
    
	char str1[] = "hello world!";//字符串存储在数组中
	char* str2 = "welcome to China!";//字符串常量赋给指针变量

	printf("%d\n", strlen(str1));
	printf("%d\n", strlen(str2));
	return 0;

1.2 strlen模拟实现

我们可以参考头文件对strlen函数的声明:size_t strlen ( const char * str );

可以发现 strlen 函数的返回值是一个无符号整型,函数参数为const修饰的指针
我们可以试着分析一下strlen的工作原理:我们知道一个字符串,是包含有 ‘\0’ 的,但是在输出长度的时候,似乎并没有把 ‘\0’ 看成一个字符。同理,‘\0’ 也是字符串的结束标志。由此不难分析出,strlen会统计字符串除 ‘\0’ 以外的字符个数

我们设计一个代码:

#include <stdio.h>
                  //此处没有头文件!
unsigned int AnalogStrlen(const char* str)
{
    
    
	const char* start = str;//定义一个用来存储首元素地址的变量
	while (*str++ != '\0')//我们要让str指向'\0'
		;//执行空语句
	return str-1 - start;//使str指向最后一个元素并进行指针运算得到中间元素个数
}
int main()
{
    
    
	char str1[] = "hello world!";
	char* str2 = "welcome to China!";

	//模拟实现strlen
	printf("%d\n", AnalogStrlen(str1));
	printf("%d\n", AnalogStrlen(str2));
	return 0;
}

2. strcpy——字符串拷贝

2.1 strcpy用法

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char str1[20] = {
    
     0 };
	char* str2 = "How to use strcpy?";
	printf("%s\n", str1);//未使用strcpy时打印
	strcpy(str1, str2);
	printf("%s\n", str1);//使用strcpy后打印
	return 0;
}

在这里插入图片描述

注意我们的参数,是先写 str1 再写 str2 。为什么?这是因为拷贝是要有方向的,这里做出的解释为:把str2 的字符串拷贝至 str1 数组中。记住,一定是字符串拷贝至数组中,因为需要足够大的空间容纳拷贝过来的字符串。可以参考 cplusplus 对此做出的解释。
在这里插入图片描述

2.2 strcpy模拟实现

我们可以观察strcpy的函数声明:char * strcpy ( char * destination, const char * source );
得到strcpy函数的返回值是一个字符指针(这个字符指针是为了防止原地址丢失),参数为一个需要拷贝进的空间地址和一个被拷贝的字符串。

相信原理不难理解:我们需要把被拷贝的字符串所有内容拷贝至数组中。

#include <stdio.h>
				  //此处无头文件!
char* AnalogStrcpy(char* dest, const char* src)
{
    
    
	char* start = dest;//记录目的地地址
	while ((*dest++ = *src++) != '\0')//将字符串的所有内容拷贝至数组中
		;
	return start;//返回地址
}
int main()
{
    
    
	char str1[20] = {
    
     0 };
	char* str2 = "How to use strcpy?";
	printf("%s\n", str1);//拷贝前
	AnalogStrcpy(str1, str2);
	printf("%s\n", str1);//拷贝后
	return 0;
}

3. strcat——字符串追加

3.1 strcat用法

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char str1[20] = "hello ";//注意o后面有一个空格
	char* str2 = "world!";
	//现在我们要使 world! 追加到 hello_ 的后面
	strcat(str1, str2);
	printf("%s\n", str1);
	return 0;
}

在这里插入图片描述
与strcpy类似的,函数的参数都有两个参数,前一个未目的地,后一个为源。也就是说,前一个参数指向的空间必须足够容纳追加后的总字符串大小

3.2 strcat模拟实现

我们观察strcat的函数声明:char * strcat ( char * destination, const char * source );
返回值为字符指针,一个目的地指针,一个源指针。

我们要剖析一下 strcat 的工作原理:
在这里插入图片描述
那么我们用代码描述:

#include <stdio.h>
				  //此处没有头文件
char* AnalogStrcat(char* dest, const char* src)
{
    
    
	char* start = dest;//记录目的地地址
	while (*dest++ != '\0')//找到 str1 的 '\0' 时 ++ 了一下
		;
	dest--;//所以这里要 -- 一下
	while ((*dest++ = *src++) != '\0')//把 str2 的内容追加过来
		;
	return start;//返回地址
}
int main()
{
    
    
	char str1[20] = "hello ";
	char* str2 = "world";
	AnalogStrcat(str1, str2);
	printf("%s\n", str1);
	return 0;
}

4. strcmp——字符串比较

4.1 strcmp用法

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char* str1 = "abcdaaa";
	char* str2 = "abce";
	int ret=strcmp(str1, str2);//strcmp具有返回值
	if (ret < 0)
		printf("str1 < str2\n");
	else if (ret > 0)
		printf("str1 > str2\n");
	else
		printf("str1 == str2\n");
	return 0;
}

在这里插入图片描述
观察代码与结果,非常诧异的一件事是:str1 的 字符串长度比 str2 的字符串长度长啊!为什么输出小于?要是这么想就大错特错,如果是为了比较字符串长度使用 strlen 函数就够了,就不需要 strcmp 函数了。

我们浏览 cplusplus 给出的解释:
在这里插入图片描述
原来,strcmp 函数比较的是两个字符串中第一次出现的不同的字符,若 str1 大于 str2 则返回一个大于 0 的数,反之则返回一个小于 0 的数,等于则返回 0 。

犹如本例比的是 ‘d’ 与 ‘e’ 的大小。

4.2 strcmp模拟实现

我么观察 strcmp 的函数声明:int strcmp ( const char * str1, const char * str2 );
得知返回值为整型,参数为两个字符串。

那么 strcmp 的工作原理是什么呢?
在这里插入图片描述
一定要注意,指针移动的前提是两个字符串的元素相同

我们用代码描述它:

#include <stdio.h>

int AnalogStrcmp(const char* str1, const char* str2)
{
    
    
	while ((*str1 - *str2 == 0) && *str2)//只有 两两元素相等 并且 str2 不为 '\0'时 指针才指向下一个位置
		str1++, str2++;

	if (*str1 > *str2)
		return 1;
	else if (*str1 < *str2)
		return -1;
	else
		return 0;
	
}
int main()
{
    
    
	char* str1 = "abcdaaa";
	char* str2 = "abce";
	int ret =AnalogStrcmp(str1, str2);
	if (ret > 0)
		printf("str1 > str2\n");
	else if (ret < 0)
		printf("str1 < str2\n");
	else
		printf("str1 == str2\n");
	return 0;
}

5. strncpy——有长度限制的字符串拷贝

5.1 strncpy用法

这个函数方便了我们只想对字符串的某一段进行拷贝,也增加了代码的安全性。用法与strcpy大同小异,只不过需要第三个参数,这个参数代表着要对几个字符进行拷贝。

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char str1[20] = {
    
    0};
	char* str2 = "hello world! welcome to China!";
	strncpy(str1, str2, 12);//只想拷贝 hello world! 
	printf("%s\n", str1);
	return 0;
}

在这里插入图片描述

5.2 strncpy模拟实现

与 strcpy 不同的是,strncpy 可能只会拷贝字符串的某一段,也就是不拷贝 ‘\0’ ,所以在模拟实现的时候我们必须要注意到这个问题。因为某些人的语法习惯是不初始哈数组,这就可能会造成数组里面一个 ‘\0’ 都没有。

我们参考一下 strncpy 的函数声明:char * strncpy ( char * 目标, const char * 源, size_t num );
可见仅仅是多了一个无符号整型参数。

那么我们试着用自己的语言来实现这个函数的功能:

#include <stdio.h>
				  //未加头文件
char* AnalogStrncpy(char* dest, const char* src, unsigned int count)
{
    
    
	char* start = dest;//记录目的地地址
	while (count && (*dest++ = *src++))//只有在 两个条件都不为 0 时才进行拷贝
		count--;
	*dest++ = 0;//拷贝完成之后需要补 '\0'
	return start;//返回目的地地址

}
int main()
{
    
    
	char str1[20];//未初始化
	char* str2 = "hello world! welcome to China!";
	AnalogStrncpy(str1, str2, 12);
	printf("%s\n", str1);
	return 0;
}

6. strncat——有长度限制的字符串追加

6.1 strncat用法

同理,与 strcar 的用法并无区别,只不过需要添加一个参数,这个参数代表需要追加几个字符串。

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char str1[20] = "hello ";
	char* str2 = "world! welcome to China!";
	strncat(str1, str2, 6);
	printf("%s\n", str1);
	return 0;
}

在这里插入图片描述

6.2 strncpy模拟实现

我们参考一下 strncpy 的函数声明:char * strncat ( char * destination, const char * source, size_t num );

我们同样也需要注意一个问题,那就是要追加的字符串末尾可能没有 ‘\0’ ,所以在我们模拟实现时,要考虑这一个问题。

模拟代码:

#include <stdio.h>
				  //无头文件
char* AnalogStrncpy(char* dest, const char* src, unsigned int count)
{
    
    
	char* start = dest;
	while (*dest++ != '\0')//寻找到 *dest 为 '\0' 又进行了一次 ++
		;
	dest--;//所以这里需要 -- 找到 '\0' 真正位置
	while (count && (*dest++ = *src++))//当两个条件都不为 0 时才赋值
		count--;
	*dest++ = 0;//下一位赋值'\0'
	return start;
}
int main()
{
    
    
	char str1[20] = "hello ";//充分的空间准备,使追加能够顺利
	char* str2 = "world! welcome to China!";
	AnalogStrncpy(str1, str2, 6);
	printf("%s\n", str1);
	return 0;
}

7. strncmp——有长度限制的字符串比较

7.1 strncmp用法

与 strcmp 一样,只不过多了一个函数参数。这个参数表示要比较的元素个数或者是次数。
在这里插入图片描述

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char* str1 = "abceef";
	char* str2 = "abcg";
	int ret=strncmp(str1, str2,4);//比较四个字符
	if (ret > 0)
		printf("str1 > str2\n");
	else if (ret < 0)
		printf("str1 < str2\n");
	else
		printf("str1 == str2\n");

	return 0;
}

在这里插入图片描述

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char* str1 = "abceef";
	char* str2 = "abcg";
	int ret=strncmp(str1, str2,3);//比较三个字符
	if (ret > 0)
		printf("str1 > str2\n");
	else if (ret < 0)
		printf("str1 < str2\n");
	else
		printf("str1 == str2\n");

	return 0;
}

在这里插入图片描述

7.2 strncmp模拟实现

我们先参考一下头文件对 strncmp 的函数声明:int strncmp ( const char * str1, const char * str2, size_t num );
与 strcmpy 函数大同小异。

我们用代码实现一下:

#include <stdio.h>

int AnalogStrncmp(const char* str1, const char* str2, unsigned int count)
{
    
    
	while (--count && (*str1 - *str2 == 0) && *str2)// count 不为 0 且 两字符串的两个元素相同才进入循环
		str1++, str2++;

	if (*str1 > *str2)
		return 1;
	else if (*str1 < *str2)
		return -1;
	else
		return 0;
}
int main()
{
    
    
	char* str1 = "abceef";
	char* str2 = "abceez";
	int ret=AnalogStrncmp(str1, str2, 5);//比较 5 个字符
	if (ret > 0)
		printf("str1 > str2\n");
	else if (ret < 0)
		printf("str1 < str2\n");
	else
		printf("str1 == str2\n");
	return 0;
}

在这里插入图片描述

8. strstr——在字符串中查找另一字符串

8.1 strstr用法

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char* str1 = "abcdefg";
	char* str2 = "cde";
	printf("%s\n", strstr(str1, str2));
	return 0;
}

在这里插入图片描述
函数的两个参数代表着:在 str1 中 查找 str2 ,如果 str1 包含 str2 ,则返回 str2 在 str1 中首次出现的的地址。否则返回空指针。
在这里插入图片描述

8.2 strstr模拟实现

如果我们想要用自己的代码实现 strstr 这个函数的功能,就必须分析工作原理。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里最大的问题在于要创建多个临时变量,那么我们通过代码体会。

#include <stdio.h>

char* AnalogStrstr(const char* str1, const char* str2)
{
    
    
	
	 char* mark = (char*)str1;//创建一个准备成为返回值的指针
	 char* s1, *s2;//移动指针 的变量
	while (*mark)//如果 *mark 为0了,证明 str1 中 不包含 str2 
	{
    
    
		s1 = mark;//使 s1 指向 mark 指向的位置
		s2 = (char*) str2;//s2 指向 str2
		while (*s1 && *s2 && (*s1 - *s2 ==0 ))//只有 *s1 *s2 且 *s1 *s2 相等时才进行查找
		{
    
    
			s1++;
			s2++;
		}
		if (*s2=='\0')//如果 *s2 为 0 则证明 str1 中包含 str2 
			return mark;//返回mark
		mark++;//如果 s2 没有找完,那么 mark 进入下一个位置继续查找
	}
	return NULL;//找不到返回空指针
}
int main()
{
    
    
	char* str1 = "abcdefg";
	char* str2 = "cde";
	AnalogStrstr(str1, str2);
	printf("%s\n", AnalogStrstr(str1, str2));
	return 0;
}

9. strtok——切割字符串

9.1 strtok用法

strtok 的定义:
在这里插入图片描述
翻译成人话就是:strtok 这个函数有两个参数,第一个是要操作的字符串,第二个是分割字符串,也就是对第一个参数的字符串扫描,碰到了分割字符串中的任意一个,就把那个字符标记成 ‘\0’ ,然后返回之前的地址。下一次想要继续寻找下一个分割符的时候,第一个参数写一个空指针就好了。

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char* str = "[email protected]";
	char* set = ".@";
	char arr[30];
	char* tmp = NULL;
	strcpy(arr, str);
	for (tmp = strtok(arr, set); tmp != NULL; tmp = strtok(NULL, set))
	{
    
    
		printf("%s\n", tmp);
	}
	return 0;
}

在这里插入图片描述

10. strerror——解析错误码

10.1strerror用法

实际上,VS 这款编译器给我们提供了一个错误码变量 int errno; 也就是说,错误信息会存储到 errno 变量当中,而 strerror 是用来解析错误码的函数

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	printf("%s\n", strerror(4));
	printf("%s\n", strerror(5));

	return 0;
}

在这里插入图片描述
具体的用法举例可以是:我们没有创建文件,但我们硬要使用文件:

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	FILE* pFile;
	pFile = fopen("unexist.ent", "r");
	if (pFile == NULL)
		printf("%s\n", strerror(errno));//错误码自动保存在 errno 变量当中
	return 0;
}

在这里插入图片描述

11. 总结

str 与 strn 的区别就在于前者是无长度限制字符串函数,后者为长度受限制字符串函数。其目的是长度受限制的字符串函数能够提高相对于无长度限制字符串函数的安全性。

本篇上述的举例用法仅仅是冰山一角,但是使用场景确实大同小异。

模拟实现并不是真正还原库函数,而是作者本人对函数的一点理解,会与库函数存在较大差异,而且可能存在一定安全风险。

猜你喜欢

转载自blog.csdn.net/weixin_59913110/article/details/125298275