【C语言】-字符函数、字符串函数、内存函数

目录

前言

一、常见字符串函数

1、strlen()求字符串长度

1.1、strlen()

2、strcpy()   strcat()   strcmp()函数

2.1、strcpy()

2.2、strcat()

2.3、strcmp()

3、strncpy()  strncat()  strncmp()函数学习使用

3.1、strncpy()

3.2、strncat()

3.3、strncmp()

4、strstr()查找子串

4.1、strstr()

5、strtok()切割字符串

5.1、strtok()

二、字符函数介绍

1、判断字符函数

1.1、iscntrl()

1.2、islower() isupper() isalpha()函数

2、字符转换函数

2.1、tolower() toupper()

三、内存函数使用

1、memcpy()  memmove()  memcmp()内存函数

1.1、memcpy()

1.2、memmove()

1.3、memcmp()

2、memset() 内存赋值函数

总结


前言

练习使用字符串求函数,并且模拟部分字符串函数


一、常见字符串函数

1、strlen()

2、strcpy()   strcat()   strcmp()长度不受限制的字符串函数

3、strncpy() strbcat() strncmp()长度受限制的字符串函数,比2中的函数安全

4、strstr()     查找子串

5、strtok()    切割字符串

1、strlen()求字符串长度

1.1、strlen()

意义:求字符串长度

size_t  strlen( const char* str)

strlen()一'\0'作为结束标志,函数返回'\0'之前出现的字符个数

返回值类型size_t表示unsigned int(无符号整形) 

1>、使用计数器

代码如下(示例):

size_t my_strlen(char* str)
{
	assert(str);  //断言,若str=NULL则会报错
	size_t cnt = 0;
	while (*str != '\0') 
    {
		cnt++;
		str++;
	}
	return cnt;
}

2>、指针-指针

代码如下(示例):

size_t my_strlen(char* str)
{
	assert(str);
	char* start = str;
	while (*str != '\0')
	{
		str++;
	}
	return str-start;
}

3>、使用递归

代码如下(示例):

size_t my_strlen(char* arr)
{
    assert(arr);
	if (*arr != '\0')
		return 1 + my_strlen(arr + 1);
	else
		return 0;
}

2、strcpy()   strcat()   strcmp()函数

2.1、strcpy()

意义:拷贝字符串

char* strcpy(char* dest,const char* sorc)

拷贝时将sorc(source)中看不到的'\0',必须一并拷贝到des中,否则出错

dest(目标destination)空间必须足够大,确保能够存放

1>、简单思路

代码如下(示例):

void my_strcpy(char* dest,const char* sorc)
{
	assert(dest);
	assert(sorc);
	while (*sorc!='\0')
	{
		*dest++ = *sorc++;  //最后的'\0'没进循环
    }
	*dest = *sorc;
}

2>、优化后

代码如下(示例):

char* my_strcpy(char* dest,const char* sorc)  //char*,返回一个函数指针,就是这个函数的地址
{
	assert(dest);
	assert(sorc);
	char* start = dest;
	while (*dest++ = *sorc++)  //当*sorc='\0'时,要发生赋值,但不进入while内部
	{                          //赋值后再判断
		;
	}
	return start;     //函数指针返回值是char*,返回dest字符串首地址
}

2.2、strcat()

意义:追加字符串

char* strcat(char* dest,const char* sorc)

sorc字符串必须以'\0'结束dest字符串空间必须足够大

不能自己给自己追加如my_strcat(arr1,arr1),因为'\0'被覆盖,无法判断结束

1>、优化后

代码如下(示例):

char* my_strcat(char* dest, const char* sorc)
{
	assert(dest && sorc);		
	char* start = dest;
	while (*dest != '\0')  //1、找到dest的'\0'的位置用socr覆盖
	{
		dest++;
	}
	while (*dest++ = *sorc++)//2、拷贝sorc字符串
	{
		;
	}
	return start;    //函数指针返回char*
}

2.3、strcmp()

意义:比较两个字符串的大小

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

abz>abc   abc<abcd   abc==abc

规则:1、一对一对地比ASCII码值,当*str1>*str2,str1字符串就大,2、当遇到'\0'和其他字符比,都是先出现'\0'的字符串小

1>、简单思路

代码如下(示例):

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2 && *str1!='\0' &&*str2!='\0')   //出循环后都是'\0' 
	{     //或者出循环后是字符'0'和另一个字符比较('\0'比其他字符ASCII码值小)                                                                       
		str1++;
		str2++;
	}
	if (*str1 == *str2 && *str1 == '\0')
	{
		return 0;
	}
	else
	{
		if (*str1 > *str2)
		{
			return 1;
		}
		else
		{
			return -1;
		}
	}
}

2>、优化后

代码如下(示例):

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2 && *str1!='\0' &&*str2!='\0')   //出循环后都是'\0' 
	{     //或者出循环后是字符'0'和另一个字符比较('\0'比其他字符ASCII码值小)                                                                       
		str1++;
		str2++;
	}
	return *str1 - *str2;  //大于等于小于的返回值都在此处实现
}

3、strncpy()  strncat()  strncmp()函数学习使用

和2、中的实现差不多,就是多了个参数(个数限制)

3.1、strncpy()

意义:实现sorc字符串前size_t(unsigned int)个字符拷贝

char* strncpy(char* dest,const char* sorc,size_count)

3.2、strncat()

意义:实现sorc字符串前size_个字符追加

char* strncat(char* dest,const char* sorc,size_t count)

3.3、strncmp()

意义:实现sorc字符串前size_t个字符和dest字符串比较

int strncmp(const char *dest,const char *sorc,size_t count)

4、strstr()查找子串

4.1、strstr()

意义:查找子串

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

规则:从母串str1中查找子串str2,若找到,就返回母串中子串所在位置的首地址,若找不到就返回NULL

1>、代码一

分为第一次就找到:abcde 和 bcd,第n次找到:abcccdef 和 ccd 或 cde

代码如下(示例):

char* my_strstr(char* str1, const char* str2)
{
	assert(str1 && str2);
	char* s1 = str1;
	char* s2 = str2;
	char* start = s1;   //第一个字符就相等时,start记录最开始的位置
	while (*s1 != '\0')
	{
		while (*s1 != *s2 && *s1 != '\0')
		{
			s1++;
		}
		start = s1;  //记录每次相等时的第一个字符,然后判断后面的字符是否相等
		while (*s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return start;
		}
		else
		{
			s1 = start + 1;//如果start开始不符合,找start下一位
			s2 = str2;
		}
	}
    return NULL;
}

2>、代码二

代码如下(示例):

char* my_strstr(char* str1, const char* str2)
{
	assert(str1 && str2);
	char* s1 = str1;
	char* s2 = str2;
	char* start = s1;   //第一个字符就相等时,start记录最开始的位置
	while (*start)
	{
		s1 = start;
		s2 = str2;
		while (*s1 == *s2 && *s1 != '\0' && *s2 != '\0')
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return start;
		}
		start++;
	}
	return NULL;
}

5、strtok()切割字符串

5.1、strtok()

意义:切割字符串

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

1、sep是一个字符串,定义了用作分隔符的字符集合

2、str是被切割的字符串

规则

一、1、第一次调用时strtok(str,esp),*str!=NULL时先找到str中第一个分隔符,将它改为'\0',然后返回头指针。2、因为会改变str字符串内容,所以需要先将str拷贝一份拿来使用

二、第二次调用时strtok(NULL,esp),第二次调用时会记住第一次调用后改为'\0'的位置,*str==NULL时,会跳过上一个被修改为'\0'的位置,找下一个分隔符

也可这样打印:

 结果展示:

二、字符函数介绍

1、判断字符函数

规则:为假返回0,为真返回非0

1.1、iscntrl()

1>、控制字符

在ASCII码中,第0~31号及第127号(共33个)是控制字符或通讯专用字符,如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(振铃)等

 2>、iscntrl()打印控制字符的ASCII码值

1.2、islower() isupper() isalpha()函数

判断字符小写、大写,或字母

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <ctype.h>

int cnt_a(char* str, int* cnt_up, int* cnt_alp)
{
	char* start = str;
	int cnt_low = 0;
	//小写
	while(*str!='\0')
	{
		if (isalpha(*str) != 0)
		{
			cnt_low++;
		}
		str++;
	}
	//大写
	str = start;
	while (*str != '\0')
	{
		if (isupper(*str) != 0)
		{
			(*cnt_up)++;
		}
		str++;
	}
	//字母
	str = start;
	while (*str != '\0')
	{
		if (isalpha(*str) != 0)
		{
			(*cnt_alp)++;
		}
		str++;
	}
	return cnt_low;
}

int main()
{
	int cnt_upper = 0;
	int cnt_alpha = 0;
	int* up = &cnt_upper;
	int* alp = &cnt_alpha;
	char a[] = "My heart is hard";
	printf("小写%d\n大写%d\n字母%d\n", cnt_a(a,up,alp), *up,*alp);
	return 0;
}

2、字符转换函数

2.1、tolower() toupper()

tolower()    -->把小写字母转换成大写字母,非小写字母则不变

toupper()   -->和上面相对应

三、内存函数使用

1、memcpy()  memmove()  memcmp()内存函数

memcpy()       内存拷贝,处理不重叠内存之间的数据拷贝(容易覆盖)

memmove()    内存追加,处理重叠内存之间的数据拷贝

memcmp()      内存比较

注意:这组函数是从内存上一个字节一个字节的处理,可以适用任意数据类型。

1.1、memcpy()

void* memcpy(void* dest,const void* src,size_t num)//const保护sorc内容不被改变

意义内存拷贝,处理不重叠内存之间数据的拷贝。参数中void*表示可以拷贝任意数据类型,函数名前的void*表示返回一个泛型指针。

规则:将sorc中的内存拷贝到dest,大小是num个字节

标准C语言下,不能自己拷贝自己如:arr[] = {1,2,3,4,5,6,7,8,9,10}    memcpy(arr+1,arr1,20)//把23456被拷贝为12345不能实现

模拟实现代码:

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;

	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

1.2、memmove()

void* memmove(void* dest,const void* src,size_t  num)

意义:内存追加,处理重叠内存之间的数据拷贝,参数类型和意义与memcpy相同

思考:strcmp拷贝有重叠,其实是因为发生了数据覆盖,比如arr[] = {1,2,3,4,5,6,7,8,9,10}    memcpy(arr+2,arr1,20)//结果是arr[] = {1,2,1,2,1,2,1,8,9,10}而不是我们要的arr[] = {1,2,1,2,3,4,5,8,9,10},就是遵循从前往后拷贝,这种情况下,src内容从后往前(dest在src后)拷贝给dest就没有问题,所以从前往后(dest在src前)拷贝,还是从后往前拷贝就很重要了。

总结:内存重叠时:dest在前,从前往后拷;dest在后,从后往前拷。

即dest位置决定拷贝方式:

 模拟实现代码:

void* my_memmove(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;

	if (dest < src)
	{
		//前-->后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest+1;
			src = (char*)src+1;
		}
	}
	else
	{
		//后-->前
		while (num--)
		{
			*((char*)dest + num) =  * ((char*)src + num);
		}
	}
	return ret;
}

1.3、memcmp()

int memcpy(const void* ptr1,const void* ptr2,size_t  num)

规则:前num个字节一个一个比较,大于返回1等于返回0,小于返回-1

2、memset() 内存赋值函数

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

参数:ptr指向要填充的内存地址,value要被设置的值,num要被设置的值的字节数

注意:memset值初始化可初始化为0,但如果初始化为1,大于一个字节的数据类型类型会出错,比如整形。原因是:memset是对字节操作会将每个字节都改为1,结果就变成一个很大的整形,看下面的例子:

正确使用例子:

 错误使用例子:

总结

针对字符串函数的内容,最重要的是要学会画图,确定1、有几种情况2、指针指向哪里3、要模拟实现应当借鉴参数、返回值的设置

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

学Java的冬瓜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值