【C进阶】常用字符串函数大全,确定不来看看吗?

“普吉岛有扶桑花的晚霞和汹涌澎湃藏着爱的海底 以及浓郁的风吹开少年椰子味的心事”

在这里插入图片描述

大家好,这里是新一,请多关照🙈🙉🙊。在本篇博客中,新一将会为大家介绍C语言字符串及内存函数,干货满满哟。(以下结果均在VS2022 - X64中编译)希望在方便自己复习的同时也能帮助到大家。😜😜😜🥇🥈🥉

废话不多说,直接进入我们的文章。



一.🥇 函数介绍

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中,字符串常量适用于那些对它不做修改的字符串函数,今天新一就来为大家介绍字符串及其内存函数。

1.1🥈 求字符串长度

这里新一只介绍最常用的strlen函数。

1.11🥉 strlen

size_t strlen ( const char * str );

#include <string.h>
int main()
{
	//strlen返回值类型是无符号数所以结果出来虽然是-3
	//但按照无符号数计算将是一个很大的正数

	if (strlen("abc") - strlen("qwerty") > 0)
	{
		printf(">\n");//>
	}
	else
	{
		printf("<=\n");
	}

	//改进

	if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");//<=
	}

	return 0;
}

注意
1. 字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
2. 参数指向的字符串必须要以 ‘\0’ 结束。
3. 注意函数的返回值为size_t,是无符号的( 易错 )

1.2🥈 长度不受限制的字符串函数

1.21🥉 strcpy

字符串拷贝函数 - strcpy

char * strcpy(char * destination, const char * source );

int main()
{
    //1.正常拷贝
	char arr1[20] = { 0 };
	char* arr2 = "abcdef";
	
	//2.常量字符串拷贝失败
    char* arr3 = "duihsaiud";//arr3指向的是常量字符串,空间不可修改
	char* arr4 = "abcdef";
	
	//3.程序奔溃
	char arr5[20] = { 0 };
	char arr6[] = {'a','b','c'};
	//char arr6[] = {'a','b','c','\0'};//正确写法
	//注意拷贝的字符串必须含有\0_否则报错
	// \0也会被拷贝进去 -  调试即可验证

	strcpy(arr1, arr2);

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

	return 0;
}

注意
1. 源字符串必须以 ‘\0’ 结束。
2. 会将源字符串中的 ‘\0’ 拷贝到目标空间。
3. 目标空间必须足够大,以确保能存放源字符串。
4. 目标空间必须可变。

1.22🥉 strcat

字符串追加函数 - strcat

char * strcat ( char * destination, const char * source );

int main()
{
	char arr1[20] = "hello\0XXXX";
	char arr2[] = "bit";
	//源字符串必须含有\0 - 调试可证明
	//目标字符串是根据\0的位置来进行追加的
	
	//报错 - 没有足够大的空间
	char arr3[5] = { 'a','b','c','e','f'};
	char arr4[] = "bit";
	
	strcat(arr1, arr2);

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

	return 0;
}

注意
1. 源字符串必须以 ‘\0’ 结束。
2. 目标空间必须有足够的大,能容纳下源字符串的内容。
3. 目标空间必须可修改

1.23🥉 strcmp

字符串比较函数 - strcmp

int strcmp ( const char * str1, const char * str2 );

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

	int ret = strcmp(arr1, arr2);
	//strcmp函数比较的不是字符串的长度
	//而是比较字符串中对应的位置的字符的ascii码值的大小,相同则比较下一对,直到不同或者都遇到\0
	printf("%d\n", ret);

	//返回值为整数 <0 =0 >0 代表小于 等于 大于

	return 0;
}

标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字

上述长度不受限制的字符串函数使用是有风险的,由于没有限制,相当于操作不会考虑空间不足等问题,导致程序奔溃

1.3🥈 长度受限制的字符串函数

1.31🥉 strncpy

受限制的字符串拷贝函数 - strncpy

char * strncpy ( char * destination, const char * source, size_t num );

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "adsuidasczx";
	
	strncpy(arr1, arr2, 3);//相比strcpy函数相对安全

	printf("%s\n", arr1);//只拷贝了前三个字符 adsdef

	return 0;
}

注意
1. 拷贝num个字符从源字符串到目标空间。
2. 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

1.32🥉 strncat

受限制的字符串追加函数 - strncat

char * strncat ( char * destination, const char * source, size_t num );

int main()
{
	char arr1[20] = "abcdef\0XXXXXXX";
	char arr2[] = "qwuqwqywiyt";

	strncat(arr1, arr2, 3);//追加指定位数后会追加一个\0-可自加
	//如果追加位数过多,该函数也不会多追加

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

	return 0;
}

注意
1. 追加num个字符到上述字符串第一个\0之后的部分
2. 如果追加太多,超过预留空间,该函数也不会强行追加而导致报错

1.33🥉 strncmp

受限制的字符串比较函数 - strncmp

int strncmp ( const char * str1, const char * str2, size_t num );

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcdq";
	int ret = strncmp(arr1, arr2, 3);

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

	return 0;
}

比较结束
1. 字符不一样
2. 其中一个字符串结束
3. num个字符全部比较完

🚀 小科普

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abc";
	if (arr1 < arr2)
	{

	}
	char* p = "abc";//当字符串作为表达式出现时,实际上返回值就是其地址

	if ("abc" < "abcdef")
	{
		//实际上比的就是地址
	}
}

上述受限制的字符串操作函数使用起来会更加安全

1.4🥈 字符串查找函数

1.41🥉 strtsr

查找子串函数 - strstr

char * strstr ( const char * str1, const char * str2);

int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "cdef";
	char* ret = strstr(arr1, arr2);
	if (NULL == ret)
	{
		printf("找不到子串\n");
	}
	else
	{
		printf("%s\n", ret);//返回地址,向后打印直到\0
		//cdefabcdef
	}

	return 0;
}

注意: 匹配成功后返回第一个字符的地址

1.42🥉 strtok

字符串切割函数 - strtok

char * strtok ( char * str, const char * sep );

tips:
1. sep参数是个字符串,定义了用作分隔符的字符集合
2. 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
3. strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
4. strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。(说明函数内有静态变量)
5. strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
6. 如果字符串中不存在更多的标记,则返回 NULL 指针。

🚀 实例

int main()
{
	char arr[] = "xzesdiaid@yeah.net nihao";
	char buf[100] = { 0 };//准备好复制字符串
	strcpy(buf, arr);//拷贝字符串

	const char* sep = " @.";//确定sep分割数组的元素

	//strtok
	printf("%s\n", strtok(buf, sep));//只找第一个标记
	printf("%s\n", strtok(NULL, sep));//从保存好的位置开始继续向后查找
	printf("%s\n", strtok(NULL, sep));//从保存好的位置开始向后找
	printf("%s\n", strtok(NULL, sep));//从保存好的位置开始向后找


	//优化算法 - 循环打印
	//char* str = NULL;
	/*for (str = strtok(buf, sep); str != NULL; str = strtok(NULL, sep))
	{
		printf("%s\n", str);
	}*/

	return 0;
}

在这里插入图片描述

1.5🥈 错误信息报告函数

1.51🥉 strerror

错误码报告函数 - strerror

我们运行代码时,肯定看到过运行框最后一行“进程已停止,代码为0”,肯定还有过其他情况,其实这可以用函数strerror来看看数字代表什么意思

#include <errno.h>

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));

	return 0;
}

在这里插入图片描述
同时该函数还能找到错误的原因,我们写完代码即可用该函数判断判断,程序有没有错误

#include <stdlib.h>
#include <limits.h>
#include <errno.h>

int main()
{
	int* p = (int*)malloc(INT_MAX);//向堆区开辟内存 - 开辟过大导致错误
	if (p == NULL)
	{
		//printf("%s\n", strerror(errno));
		//perror更方便
		perror("malloc");//需要传一个字符串,随便写就行

		return 1;
	}

	return 0;
}

在这里插入图片描述

1.6🥈 字符操作函数

1.61🥉 字符分类函数

在这里插入图片描述

#include <ctype.h>

int main()
{
	int ret = isdigit('a');//判断数字
	printf("%d\n", ret);

	/*char ch = '1';
	int ret = islower(ch);

	printf("%d\n", ret);*///只要打印出来为非0数字,那么就为真

	return 0;
}

1.62🥉 字符转换函数

在这里插入图片描述

int main()
{
	char ch = 'A';
	putchar(toupper(ch));//转大写
	putchar(tolower(ch));//转小写

	return 0;
}

1.7🥈 内存操作函数

1.71🥉 memcpy

内存复制函数 - memcpy

void * memcpy ( void * destination, const void * source, size_t num );

tips:
1. 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
2. 这个函数在遇到 ‘\0’ 的时候并不会停下来。
3. 如果source和destination有任何的重叠,复制的结果都是未定义的。

//作者实现memcpy函数的时候要符合所有类型的情况,故返回值为void *

/* memcpy example */
#include <stdio.h>
#include <string.h>
struct {
	char name[40];
	int age;
} person, person_copy;
int main()
{
	char myname[] = "Pierre de Fermat";
	//using memcpy to copy string :
	memcpy(person.name, myname, strlen(myname) + 1);//复制名字
	person.age = 46;
	//using memcpy to copy structure: 
	memcpy(&person_copy, &person, sizeof(person));//复制整个person结构体
	printf("person_copy: %s, %d \n", person_copy.name, person_copy.age);
	return 0;
}

在这里插入图片描述

1.72🥉 memmove

重叠内存拷贝函数 - memmove

void * memmove ( void * destination, const void * source, size_t num );

tips:
1. 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
2. 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

/* memmove example */
#include <stdio.h>
#include <string.h>
int main ()
{
  char str[] = "memmove can be very useful......";
  memmove (str+20,str+15,11);
  puts (str);
  return 0; 
}

在这里插入图片描述

1.73🥉 memcmp

内存比较函数 - memcmp

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

tips:
比较从ptr1和ptr2指针开始的num个字节

在这里插入图片描述

#include <stdio.h>
#include <string.h>
int main()
{
	char buffer1[] = "DWgaOtP12df0";
	char buffer2[] = "DWGAOTP12DF0";
	int n;
	n = memcmp(buffer1, buffer2, sizeof(buffer1));
	if (n > 0) printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
	else if (n < 0) printf("'%s' is less than '%s'.\n", buffer1, buffer2);
	else printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
	return 0;
}

在这里插入图片描述

1.74🥉 memset

内存设置函数 - memset

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

tips:
1. 以字节为单位来初始化内存单元为value
2. 表示给指针ptr初始化num个内存空间

int main()
{
	int arr[] = { 0x11223344,0x22222222,3,4,5 };
	memset(arr, 0xf, 20);

	return 0;
}

初始化前
在这里插入图片描述
初始化后
在这里插入图片描述
这也就方便了我们为数组做初始化

二.🥇 模拟实现

2.1🥈 模拟实现strlen

🚀 计数器方式

//计数器方式
int my_strlen(const char * str) {
 int count = 0;
 while(*str)
 {
 count++;
 str++;
 }
 return count; }

🚀 递归

//不能创建临时变量计数器
int my_strlen(const char * str) {
 if(*str == '\0')
 return 0;
 else
 return 1+my_strlen(str+1);
}

🚀 指针 - 指针

//指针-指针的方式
int my_strlen(char *s) {
       char *p = s;
       while(*p != ‘\0)
              p++;
       return p-s; 
}

2.2🥈 模拟实现strcpy

tips:
1.参数顺序
2.函数的功能,停止条件
3.assert
4.const修饰指针
5.函数返回值

char *my_strcpy(char *dest, const char*src)
{ 
 char *ret = dest;
 assert(dest != NULL);
 assert(src != NULL);
 
 while((*dest++ = *src++))
 {
 ;
 }
 return ret; 
 }

2.3🥈 模拟实现strcat

char *my_strcat(char *dest, const char*src) {
 char *ret = dest;
 assert(dest != NULL);
 assert(src != NULL);
 while(*dest)
 {
 dest++;
 }
 while((*dest++ = *src++))
 {
 ;
 }
 return ret; 
 }

2.4🥈 模拟实现strstr

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	
	const char* s1 = str1;
	const char* s2 = str2;

	const char* cur = str1;
	while (*cur)
	{
		s1 = cur;
		s2 = str2;

		while ((*s1 == *s2) && *s1 && *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)cur;
		}
		cur++;
	}

	return NULL;
} 

其实还有更高效的算法😎😎😎,那就是我们的KMP算法了,感兴趣的友友可以移步这篇博客KMP算法保姆级教程

2.5🥈 模拟实现strcmp

int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 && s2);
	int ret = 0;
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
		{
			return 0;
		}
		s1++;
		s2++;
	}
	//不相等
	return *s1 - *s2;
}

2.6🥈 模拟实现memcpy

void* my_memcpy( void* dest, const void* src, size_t count )
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}

2.7🥈 模拟实现memmove

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

	void* ret = dest;
	if (dest < src)
	{
		//从前向后拷贝
		while (count--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		//从后向前拷贝
		while (count--)
		{
			*((char*)dest + count) = *((char*)src + count);
		}
	}

	return ret;
}

想对大家说的话

家人们,学到这里我们已经理解了字符串函数的相关内容,相信以后也知道如何做利用有关的函数了🥳🥳🥳后续新一会持续更新C语言的有关内容,学习永无止境,技术宅,拯救世界!
在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Corwttaml

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值