C语言之字符函数和字符串函数

文章详细介绍了C语言中处理字符串和字符的几个关键函数,包括strlen用于计算字符串长度,strcpy和strcat分别用于字符串拷贝和追加,strcmp进行字符串比较,以及内存操作函数memcpy和memmove。同时,文章通过模拟实现这些函数,强调了它们的使用注意事项和潜在的安全问题,如目标空间大小、字符串结束标志以及处理重叠内存区域的情况。
摘要由CSDN通过智能技术生成

本章重点:
重点介绍处理字符和字符串函数的库函数的使用以及注意事项

求字符串长度
strlen
长度不受限制的字符串函数
strcpy
strcat
strcmp
长度受限制的字符串函数介绍
strncpy
strncat
strncmp
字符串查找
strstr
strtok
错误信息报告
strerror
字符操作
内存操作函数

memcpy
memmove
memset
memcmp

1.函数介绍

1.1strlen

在库函数中我们可以查到 strlen函数 参数类型,返回值类型如下所示:

size_t strlen( const char *string );

size_t 是无符号整形,只不过是类型重定义了 unsigned int 为size_t。需要我们注意的的是 形参 是 char *string 接收 则传给strlen函数的参数应该是地址。
且字符串以 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’
用三种方法模拟实现strlen

#include<stdio.h>
//法1计数法
size_t Count_arr(char* pa)
{
	int n = 0;
	while (*pa++ != '\0')
	{
		n++;
		
	}
	return n;
}
//法2 指针 - 指针
size_t Point_arr(char* pa)
{
	char* start = pa;
	while (*pa!= '\0')
	{
		pa++;
	}
	return pa - start;

}
// 法3递归
size_t Gradu_arr(char* pa)
{
	if (*pa != '\0')
		return 1 + Gradu_arr(pa + 1);
	else
		return 0;
}

void test1()
{
	char arr1[] = "nihao,zhegeshijie";

	size_t ret = Count_arr(arr1);
	printf("%zd\n", ret);


}
void test2()
{
	char arr2[] = "nihao,zhegeshijie";

	size_t ret = Point_arr(arr2);
	printf("%zd\n", ret);


}
void test3()
{
	char arr3[] = "nihao,zhegeshijie";

	size_t ret = Gradu_arr(arr3);
	printf("%zd\n", ret);


}
//模拟实现strlen
int main()
{
	
	//test1();
	//test2();
	test3();

	return 0;
}

1.2strcpy

字符串拷贝函数,先看看原型char *strcpy( char *strDestination, const char *strSource ); 因为是拷贝所以用了两个指针变量接收,需要注意的是源地址的形参用了const修饰,所以源地址的内容不可被修改,且函数的返回类型也是cha*类型。这里有四点需要注意:
源字符串必须以 ‘\0’ 结束。
会将源字符串中的 ‘\0’ 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。(不可是常量字符串,因为常量字符串不可被修改)
目标空间必须可变。

模拟实现 strcpy

char* my_strcpy(char* des, const char* sor)
{
	char *pos = des;
	while (*des++ = *sor++ )
	{
		;
	}
	return pos;
}
int main()
{
	char arr1[] = "xxxxxxxxxxxx";
	char arr2[] = "nihao";
	char *ret = my_strcpy(arr1, arr2);
	printf("%s\n",ret);


	return 0;
}

strcat

strcat是追加的函数,在字符串后面追加字符串 原型如下:char *strcat( char *strDestination, const char *strSource );跟strcpy是一样的
需要注意的是:
源字符串必须以 ‘\0’ 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改

模拟实现strcat

#include<stdio.h>

char* my_strcat(char* des, const char* sor)
{
	char* pos = des;
	while (*des)
	{
		des++;
	}
	while (*des++ = *sor++)
	{
		;
	}
	return pos;
}
int main()
{

	char arr1[80] = "nihao ";
	char arr2[] = "shijie";
	printf("%s\n",my_strcat(arr1, arr2));
	return 0;
}

因为是在目标空间里寻找\0所以 如果是自己给自己追加就会出现问题 所以没有觉得安全的函数,需要我们自己去让他安全的使用;

1.4strcmp

字符串拷贝函数 原型为:int strcmp( const char *string1, const char *string2 ); 因为只是比较两个字符串 所以两个形参 都是用const修饰,需要注意的是返回值是int类型且比较的是两个字符串的ascii码值,标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字

模拟实现strcmp

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


int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	
	while (*str1 && *str2 && *str1 == *str2)
	{
		str1++;
		str2++;
	}
	return *str1 - *str2;
}
int main()
{

	char arr1[] = "nihaoya";
	char arr2[] = "ni";

	int ret = my_strcmp(arr1, arr2);

	if (ret > 0)
		printf("arr1大于arr2\n");
	else if(ret < 0)
		printf("arr1小于arr2\n");
	else
		printf("arr1等于arr2\n");
}

strncat

指定追加个数字符串函数,原型为char *strncat( char *strDest, const char *strSource, size_t count );模拟实现只是有个限制条件count

strncmp

指定比较字符串个数函数,原型为char *strncat( char *strDest, const char *strSource, size_t count );模拟实现在比较部分 加上限制条件count

strstr

在某个字符串中寻找 字符或字符串函数,原型为:char *strstr( const char *string, const char *strCharSet );需要注意的是:
在这里插入图片描述
模拟实现

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


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

	if (*str2 == '\0')
	{
		return (char*)str1;
	}

	const char* s1 = str1;
	const char* s2 = str2;
	const char* cp = str1;
	//用cp记录两个字符串第一个相同的位置
	while (*cp)
	{
		s1 = cp;// 未完成匹配 将记录的起始+1位置 给s1
		s2 = str2;//未完成匹配 回到起始位置
		//指针指向位置不为空 且相等的时候 进行前移操作
		while (*s1  && *s2  && *s1 == *s2)
		{
			s1++;
			s2++;
		}
	   //str2 移动指针已经遍历完 说明在str1中已经有了 返回起始指针
		if (*s2 == '\0')
		{
			return(char*) cp;
		}
		//如果是从相等那层循环跳出,说明未能完成完全匹配 将str1中记录起始位置的cp往后移动一位,如果都咩进去那层循环,说明也不匹配
		cp++;
	}
	return NULL;

}
int main()
{

	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";

	char*ret = my_strstr(arr1, arr2);

	if (ret)
		printf("arr1中有arr2 :%s\n", ret);
	else
		printf("arr1中没有arr2\n");
		
	return 0;
}

strtok

切割函数,原型为:char *strtok( char *str, const char *sep );需要注意的是:
sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标
记。
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:
strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容
并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串
中的位置。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标
记。
如果字符串中不存在更多的标记,则返回 NULL 指针
用一段代码 将上面提的重点都罗列进去:

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

int main()
{

	char *p = "nihao。shijie@wohenkaixin";//要处理的字符串
	char* sep = "。@";//切割的字符符号
	char* str = NULL;//定义一个用来输出打印的指针 初识为空
	char arr[80];//用来拷贝字符串,因为strtok会修改字符串,所以拷贝一份
	strcpy(arr, p);//进行拷贝
	//用for循环将所有字符依次打印出来
	for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
	{
		printf("%s\n", str);
	}

	return 0;
}

在这里插入图片描述

strerror

返回错误码,对应的错误信息
需要注意的是 对应的头文件为<errno.h>常见的错误信息有:
在这里插入图片描述
还有一些零碎的 字符分类函数
在这里插入图片描述
字符串转换

int tolower(int c);
int toupper(int c);

1.11memcpy

内存拷贝函数 原型为void *memcpy( void *dest, const void *src, size_t num);
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 ‘\0’ 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的

模拟实现

#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* src, size_t count)
{
	char* ret = (char*)dest;
	assert(dest && src);
	while (count--)
	{
		*(char*)dest = *(char*)src;
		++(char*)dest;
		++(char*)src;
	}
	return ret;
}

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

	my_memcpy(arr3, arr3+2, 20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr3[i]);
	}
	return 0;
}

之前说过任何函数都是不安全的,memecpy函数面对重叠拷贝的函数也是无能为力 所以就衍生出来 memmove

1.12memove

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

memove最大的改进就是能够处理折叠比分的拷贝
在这里插入图片描述

当源地址 小于目的地址时候,我们可以用memcpy实现,但是 当源地址 大于等于目的地址时候 如果还是按照memcpy操作的话 会出现 想要移动的数字,被之前的移动的数字的占用在这里插入图片描述
模拟实现 memove

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

void* my_memove(void* dest, const void* src, int count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest < src)
	{
		while (count--)
		{
			*(char*)dest = *(char*)src;
			++(char*)dest;
			++(char*)src;
		}

	}
	else
	{
		while (count--)
		{
			*((char*)dest + count) = *((char*)src + count);
		}
	}
	return ret;
}
void test1()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memove(arr1, arr1 + 2, 20);

	int sz = sizeof(arr1) / sizeof(arr1[0]);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr1[i]);
	}

}
void test2()
{
	int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memove(arr2+2, arr2, 20);

	int sz = sizeof(arr2) / sizeof(arr2[0]);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr2[i]);
	}

}
int main()
{
	
	//test1();
	test2();
		

	return 0;
}

1.13memcmp

内存比较函数,上面提到的strcmp是字符串比较 memcmp是内存比较,原型为int memcmp( const void *buf1, const void *buf2, size_t count )只是在实现的时候 将void*强制类型转换就行,返回值类型跟strcmp一样

1.14memset

以字节为单位的初始化 他只能以字节为单位 所以也不绝对安全 例如:

memset (arr,0,20
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值