深扒头文件 <string.h> 中的函数 ------- C语言

在日常敲代码的过程中,当我们处理字符串时经常会使用字符串函数,调用字符串函数则需要包含头文件 <string.h>,对于头文件 <string.h> 中包含的函数你了解多少呢?本篇博客总结了头文件 <string.h> 中一些常用的函数及其使用实例和部分函数的模拟实现,相信跟随本篇博客一起探讨,能让你对头文件 <string.h> 中的某些函数有不一样的理解。

da892362528749288c613857ac140b83.jpeg


目录

1. strlen ( 求字符串长度 )

1.1. strlen函数使用实例

 1.2. 实现my_strlen函数

2. strcpy  ( 字符串拷贝 )

2.1. strcpy函数使用实例

2.2. 实现my_strcpy函数

3. strcat ( 字符串追加 )

3.1. strcat函数使用实例

3.2. 实现my_strcat函数

4. strcmp ( 字符串比较 ) 

4.1. strcmp函数使用实例

 4.2. 实现my_strcmp函数

5. strncpy ( 限制个数的字符串拷贝 )

5.1. strncpy函数使用实例

5.2. 实现my_strncpy函数

6. strncat ( 限制个数的字符串追加 )

 6.1. strncat函数使用实例

6.2. 实现my_strncat函数

​编辑

7. strncmp ( 限制个数的字符串比较 ) 

7.1. strncmp函数使用实例

7.2. 实现my_strncmp函数

8. strstr ( 查找子字符串 )

8.1. strstr函数使用实例

 8.2. 实现my_strstr函数

9. strtok ( 切割字符串 )

9.1. strtok函数使用实例

10. memcpy ( 拷贝两块独立内存 )

10.1. memcpy函数使用实例

 10.2. 实现my_memcpy函数

11. memmove ( 可拷贝重复内存 )

11.1. memmove函数使用实例

11.2. 实现my_memmove函数

12. memcmp ( 内存比较 )

12.1. memcmp函数使用实例

​编辑

13. memset ( 内存初始化 )

13.1. memset函数使用实例

13.2. 实现my_memset函数


1. strlen ( 求字符串长度 )

size_t strlen( const char* string );

● 字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数 ( 不包括 '\0' )

● 参数指向的字符串必须要以 '\0' 结束,否则strlen函数的返回值是随机值

● strlen函数的返回值的类型为size_t,是无符号类型 ( 易错点 )

| 例 |

#include <stdio.h>
#include<string.h>

int main()
{
    const char* str1 = "bdc";
    const char* str2 = "abcdef";
    if (strlen(str1) - strlen(str2) > 0)
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    return 0;
}

如果对strlen函数返回值的类型不加注意,就会产生BUG,上面这段代码就是一个非常经典的案例。

1.1. strlen函数使用实例

#include <stdio.h>
#include<string.h>

int main()
{
    char str[] = "hello world!";
    int len = strlen(str);
    printf("len = %d\n", len);
    return 0;
}

db0c6a5f165e4194af111245b5d3a912.png

 1.2. 实现my_strlen函数

#include<stdio.h>
#include<assert.h>

//size_t my_strlen(const char* p)     //1. 计数器型
//{
//	assert(p);
//	size_t count = 0;
//	while (*p)
//	{
//		count++;
//		p++;
//	}
//	return count;
//}

//size_t my_strlen(const char* p)     //2. 指针减指针型
//{
//	assert(p);
//	char* start = p;
//	while (*p)
//	{
//		p++;
//	}
//	return p - start;
//}

size_t my_strlen(const char* p)     //3. 递归型
{
	assert(p);
	if (*p)
	{
		return 1 + my_strlen(p + 1);
	}
	else
	{
		return 0;
	}
}

int main()
{
	char str[] = "hello world!";
	int len = my_strlen(str);
	printf("len = %d\n", len);
	return 0;
}

1193770f0dfe4c97b62971ee88b47bd4.png

2. strcpy  ( 字符串拷贝 )

char* strcpy( char* strDestinationconst char* strSource );

● strcpy函数的返回值为目标字符串空间的起始地址

● 源字符串必须以 '\0' 为结尾,strcpy函数会将源字符串中的字符一一拷贝至目标字符串中,包括 '\0' ( 在拷贝过程中第一次将 '\0' 拷贝完后则停止拷贝 )

● 目标字符串的空间必须足够大,以确保能存放源字符串

● 目标字符串必须可修改 ( 即不被 const 修饰 )

2.1. strcpy函数使用实例

#include<stdio.h>
#include<string.h>

int main()
{
	char str1[] = "hello world!";
	char str2[20] = { 0 };
	strcpy(str2, str1);
	printf("%s\n", str2);
	return 0;
}

cf5b047ceef94c03b5d935fbc66c2614.png

2.2. 实现my_strcpy函数

#include<stdio.h>
#include<assert.h>

char* my_strcpy(char* dst, const char* src)
{
	assert(dst && src);
	char* start = dst;
	while (*dst++ = *src++)
	{
		;
	}
	return start;
}

int main()
{
	char str1[] = "hello world!";
	char str2[20] = { 0 };
	my_strcpy(str2, str1);
	printf("%s\n", str2);
	return 0;
}

4173b0f983734407a6d8250475887ccf.png

3. strcat ( 字符串追加 )

char* strcat( char* strDestination, const char* strSource );

● strcat函数的返回值为目标字符串空间的起始地址

● 目标字符串的空间必须足够大,以确保能存放追加后的字符串

● 目标字符串必须可修改 ( 即不被 const 修饰 )

● 源字符串必须以 '\0' 为结尾,strcat函数以将源字符串的 '\0' 追加至目标字符串空间作为追加停止条件

● strcat函数的目标字符串空间和源字符串空间不能为同一块内存空间,即不能自己给自己追加 ( 易错点 )

9a6e4d3325c84b90aaec9c765424e15b.gif

以上动图是使用strcat函数自己给自己追加的情况,当dst指向目标字符串的 '\0' 时开始追加,因为目标字符串空间与源字符串空间为同一内存空间,当开始追加后,源字符串中的 '\0' 会被覆盖,从而导致一直循环追加下去,无法按照预期停止追加。( 在VS2022的环境下,编译器对该情况继续了优化,不会出现上述动图的情况,但仍然不建议使用strcat函数自己追加自己 )

3.1. strcat函数使用实例

#include<stdio.h>
#include<string.h>

int main()
{
	char str1[30] = "hello ";
	char str2[] = "world!";
	strcat(str1, str2);
	printf("%s\n", str1);
	return 0;
}

00534da460ef4d98827381139031c501.png

  

3.2. 实现my_strcat函数

#include<stdio.h>
#include<assert.h>

char* my_strcat(char* dst, const char* src)
{
	assert(dst && src);
	char* start = dst;
	while (*dst)
	{
		dst++;
	}
	while (*dst++ = *src++)
	{
		;
	}
	return start;
}

int main()
{
	char str1[30] = "hello ";
	char str2[] = "world!";
	my_strcat(str1, str2);
	printf("%s\n", str1);
	return 0;
}

55dd27edb33740f0872126a8037e081b.png

4. strcmp ( 字符串比较 ) 

 int strcmp( const char* string1, const char* string2 );

● 标准规定:

         ▢ string1 大于 string2,则返回大于0的数字;

         ▢ string1 等于 string2,则返回0;

         ▢ string1 小于 string2,则返回小于0的数字;

4.1. strcmp函数使用实例

#include<stdio.h>
#include<string.h>

int main()
{
	char str1[] = "abcd";
	char str2[] = "efg";
	if (strcmp(str1, str2) > 0)
	{
		printf("str1 > str2\n");
	}
	else if (strcmp(str1, str2) < 0)
	{
		printf("str1 < str2\n");
	}
	else 
	{
		printf("str1 = str2\n");
	}
	return 0;
}

51e25cb3b0fb4fe5941699e01f29d5f6.png

 4.2. 实现my_strcmp函数

#include<stdio.h>
#include<assert.h>

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 str1[] = "abcd";
	char str2[] = "efg";
	if (my_strcmp(str1, str2) > 0)
	{
		printf("str1 > str2\n");
	}
	else if (my_strcmp(str1, str2) < 0)
	{
		printf("str1 < str2\n");
	}
	else 
	{
		printf("str1 = str2\n");
	}
	return 0;
}

ab6b3057156544b394fd783aabbb3ba2.png

5. strncpy ( 限制个数的字符串拷贝 )

char* strncpy( char* strDestination, const char* strSource, size_t num );

● strncpy函数的返回值为目标字符串空间的起始地址

● 从源字符串中拷贝num个字符到目标字符串空间中

● 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标字符串后面追加0,直到num个。

5.1. strncpy函数使用实例

#include<stdio.h>
#include<string.h>

int main()
{
	char str1[] = "hello world!";
	char str2[20] = { 0 };
	strncpy(str2, str1, 5);
	printf("%s\n", str2);
	return 0;
}

5.2. 实现my_strncpy函数

#include<stdio.h>
#include<assert.h>

char* my_strncpy(char* dst, const char* src, size_t num)
{
	assert(dst && src);
	char* start = dst;
	while (*src && num--)
	{
		*dst++ = *src++;
	}
	while (num--)
	{
		*dst++ = 0;
	}
	return start;
}

int main()
{
	char str1[] = "hello world!";
    char str2[20] = { 0 };
	my_strncpy(str2, str1, 8);
	printf("%s", str2);
	return 0;
}

6. strncat ( 限制个数的字符串追加 )

char* strncat( char* strDestination, const char* strSource, size_t num );

● strncat函数的返回值为目标字符串空间的起始地址

● 当源字符串长度小于num时,strncat函数不会像strncpy那样在目标字符串后面补充0

● 在目标字符串后面追加完成后,会自动在目标字符串末尾加上 '\0'

 6.1. strncat函数使用实例

#include<stdio.h>
#include<string.h>

int main()
{
	char str1[20] = "hello ";
	char str2[] = "world!";
	strncat(str1, str2, 3);
	printf("%s\n", str1);
	return 0;
}

6.2. 实现my_strncat函数

#include<stdio.h>
#include<assert.h>

char* my_strncat(char* dst, const char* src, size_t num)
{
	assert(dst && src);
	char* start = dst;
	while (*dst)
	{
		dst++;
	}
	while (*src && num--)
	{
		*dst++ = *src++;
	}
	*dst = '\0';
	return start;
}

int main()
{
	char str1[20] = "hello ";
	char str2[] = "world!";
	my_strncat(str1, str2, 3);
	printf("%s\n", str1);
	return 0;
}

7. strncmp ( 限制个数的字符串比较 ) 

int strncmp( const char* string1, const char* string2, size_t num ); 

● 标准规定:

         ▢ string1 大于 string2,则返回大于0的数字;

         ▢ string1 等于 string2,则返回0;

         ▢ string1 小于 string2,则返回小于0的数字;

7.1. strncmp函数使用实例

#include<stdio.h>
#include<string.h>

int main()
{
	char str1[] = "abbdff";
	char str2[] = "abbcde";
	if (strncmp(str1, str2, 4) > 0)
	{
		printf("str1 > str2\n");
	}
	else if (strncmp(str1, str2, 4) < 0)
	{
		printf("str1 < str2\n");
	}
	else
	{
		printf("str1 = str2\n");
	}
	return 0;
}

7.2. 实现my_strncmp函数

#include<stdio.h>
#include<assert.h>

int my_strncmp(const char* str1, const char* str2, size_t num)
{
	assert(str1 && str2);
	for (num -= 1; num && *str1++ == *str2++; num--)
	{
		if (*str1 == '\0')
		{
			return 0;
		}
	}
	return (*str1 - *str2);
}

int main()
{
	char str1[] = "abbdff";
	char str2[] = "abbcde";
	if (my_strncmp(str1, str2, 3) > 0)
	{
		printf("str1 > str2\n");
	}
	else if (my_strncmp(str1, str2, 3) < 0)
	{
		printf("str1 < str2\n");
	}
	else
	{
		printf("str1 = str2\n");
	}
	return 0;
}

8. strstr ( 查找子字符串 )

char* strstr( const char* string1, const char* string2 );

● string1为目标字符串,string2为待查的子字符串

● 若在string1中有子字符串string2,则返回在string1中子字符串的第一个字符的地址;若在string1中没有找到对应的子字符串string2,则返回NULL ( 例如:string1 = abaacde,string2 = baa,string1中存在子字符串string2,则返回在string1中的字符 'b' 的地址 )

8.1. strstr函数使用实例

#include<stdio.h>
#include<string.h>

int main()
{
	char str1[] = "hello world!";
	char str2[] = "lo wo";
	char* pc = strstr(str1, str2);
	printf("%s\n", pc);
	return 0;
}

 8.2. 实现my_strstr函数

#include<stdio.h>
#include<assert.h>

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	const char* s1 = str1;
	const char* s2 = str2;
	const char* p = str1;
	while (*p)
	{
		s1 = p;
		s2 = str2;
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return p;
		}
		p++;
	}
	return NULL;
}


int main()
{
	char str1[] = "hello world!";
	char str2[] = "lo wo";
	char* pc = my_strstr(str1, str2);
	printf("%s\n", pc);
	return 0;
}

9. strtok ( 切割字符串 )

char* strtok( char* strToken, const charstrDelimit );

● 参数strDelimit是一个字符串,定义了用作分隔符的字符集合

● 第一个参数strToken是一个字符串,它包含了0个或者多个由strDelimit字符串中一个或者多个分隔符分割的标记

● strtok函数找到strToken中的下一个标记,并将其用 '\0' 结尾,返回一个指向这个标记的指针 ( 注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改 )

● 若strtok函数的第一个参数不为NULL,函数将找到strToken中第一个标记,strtok函数将保存它在字符串中的位置

● 若strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记

● 如果strToken中不存在更多的标记,则返回NULL指针

9.1. strtok函数使用实例

#include<stdio.h>
#include<string.h>

int main()
{
	char email[] = "NICKnK579008@outlook.com";
	char key[] = "@.";
	char cp[30] = { 0 };
	strcpy(cp, email);

	//char* ret = strtok(cp, key);
	//printf("%s\n", ret);
	//ret = strtok(NULL, key);
	//printf("%s\n", ret);
	//ret = strtok(NULL, key);
	//printf("%s\n", ret);

	//以上代码过于冗余,可对以上代码进行优化

	char* ret = NULL;
	for (ret = strtok(cp, key); ret != NULL; ret = strtok(NULL, key))       1.利用for循环的初始化只执行一次的逻辑,匹配第
                                                                              一次调用strtok函数第一个参数要传字符串地址这一规定
                                                                            2.利用for循环的调整部分,后续调用strtok函数时
                                                                              将NULL作为第一个参数传递
                                                                            3.以strtok返回值为NULL作为for循环的终止条件
	{
		printf("%s\n", ret);
	}
	return 0;
}

10. memcpy ( 拷贝两块独立内存 )

void* memcpy( void* strDestination, const void* strSource, size_t num );

● memcpy函数的返回值为目标空间的起始地址

● strDestination的空间要足够大,以至于能存放得下strSource的内容

● 从strSource的位置开始向后复制num个字节的数据到strDestination的内存位置

● memcpy函数在拷贝过程中遇到 '\0' 的时候并不会停下来

● 如果strSource和strDestination有任何的重叠部分,拷贝的结果都是标准未定义的

10.1. memcpy函数使用实例

#include<stdio.h>
#include<string.h>

int main()
{
	double farr[] = { 1.2,4.5,3.3,5.7,0.2,3.0 };
	double cp[10] = { 0.0 };
	memcpy(cp, farr, sizeof(farr[0])*4);
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%.1lf\n", cp[i]);
	}
	return 0;
}

 10.2. 实现my_memcpy函数

#include<stdio.h>
#include<assert.h>

void* my_memcpy(void* dst, const void* src, size_t num)
{
	assert(dst && src);
	void* start = dst;
	while (num--)
	{
		*(char*)dst = *(char*)src;
		dst = (char*)dst + 1;
		src = (char*)src + 1;
	}
	return start;
}

int main()
{
	double farr[] = { 1.2,4.5,3.3,5.7,0.2,3.0 };
	double cp[10] = { 0.0 };
	my_memcpy(cp, farr, sizeof(double) * 4);
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%.1lf\n", cp[i]);
	}
	return 0;
}

11. memmove ( 可拷贝重复内存 )

void* memmove( void* strDestination, const void* strSource, size_t num );

● 和memcpy函数的差别就是memmove函数处理的源内存空间和目标内存空间是可以重叠的

● 如果源内存空间和目标内存空间有重叠部分,就得使用memmove函数处理

11.1. memmove函数使用实例

#include<stdio.h>
#include<string.h>

int main()
{
	char str[] = "hello world!";
	memmove(str + 7, str + 1, 4);
	printf("%s\n", str);
	return 0;
}

11.2. 实现my_memmove函数

#include<stdio.h>
#include<assert.h>

void* my_memmove(void* dst, const void* src, size_t num)
{
	assert(dst && src);
	void* start = dst;
	if (dst < src)
	{
		while (num--)
		{
			*(char*)dst = *(char*)src;
			dst = (char*)dst + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		while (num--)
		{
			*((char*)dst + num) = *((char*)src + num);
		}
	}
	return start;
}

int main()
{
	char str[] = "hello world!";
	my_memmove(str + 7, str + 1, 4);
	printf("%s\n", str);
	return 0;
}

12. memcmp ( 内存比较 )

int memcmp( const void* ptr1, const void* ptr2, size_t num );

● 从指针变量ptr1和指针变量ptr2开始向后对num个字节的数据按字节一一进行对比

● 遇到不同的数据时,会提前终止比较,后面的数据不再进行比较

● 标准规定:

         *ptr1 大于 *ptr2,则返回大于0的数字;

         ▢ *ptr1 等于 *ptr2,则返回0;

         ▢ *ptr1 小于 *ptr2,则返回小于0的数字;

12.1. memcmp函数使用实例

#include<stdio.h>
#include<string.h>

int main()
{
	float farr1[] = { 3.1f,4.4f,5.3f,6.2f,0.2f };
	float farr2[] = { 3.1f,4.4f,2.5f,4.8f };
	if (memcmp(farr1, farr2, sizeof(float) * 4) > 0)
	{
		printf("farr1 > farr2\n");
	}
	else if (memcmp(farr1, farr2, sizeof(float) * 4) < 0)
	{
		printf("farr1 < farr2\n");
	}
	else
	{
		printf("farr1 = farr2\n");
	}
	return 0;
}

13. memset ( 内存初始化 )

void* memset( void* ptr, int value, size_t num );

● memset函数的返回值为ptr空间的起始地址

● 对num个字节初始化,每个字节的值初始化成value

13.1. memset函数使用实例

#include<stdio.h>
#include<string.h>

int main()
{
	char str[10] = { 0 };
	memset(str, '$', 10);
	return 0;
}

13.2. 实现my_memset函数

#include<stdio.h>
#include<assert.h>

void* my_memset(void* ptr, int value, size_t num)
{
	assert(ptr);
	void* start = ptr;
	while (num--)
	{
		*(char*)ptr = value;
		ptr = (char*)ptr + 1;
	}
	return start;
}

int main()
{
	char str[10] = { 0 };
	my_memset(str, '$', 10);
	return 0;
}


本次与大家一起探讨头文件 <string.h> 中常用的函数到这就已经接近尾声了,期待下次与你相遇。

< 你的关注点赞评论收藏都是对我创作最大的鼓励 > 

( 若本篇博客存在错误,望指出,感谢! ) 

  • 16
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值