ctype.h的了解,string.h库函数、memcpy,memmove函数的使用和模拟实现,

目录

字符分类函数 

函数讲解 

使用样例

字符转换函数

函数讲解

使用样例

strlen的使用和模拟实现

strlen的使用

strlen的模拟实现

计数器

递归(不创建临时变量)

指针-指针

strcpy的使用和模拟实现

strcpy的使用

strcpy的模拟实现

strcat的使用和模拟实现

strcat的使用

strcat的模拟实现

strcmp的使用和模拟实现

strcmp的使用

strcmp的模拟实现

strstr的使用和模拟实现

strstr的使用

strstr的模拟实现

strncpy的使用和模拟实现

strncpy的使用

strncpy的模拟实现

strncat的使用和模拟实现

strncat的使用

strncat的模拟实现

strncmp的使用和模拟实现

strncmp的使用

strncmp的模拟实现

memcpy的使用和模拟实现

memcpy的使用 

memcpy的模拟实现 

memmove的使用和模拟实现

memmove的使用

memmove的模拟实现

memcpy和memmove的区别


字符分类函数 

函数如果它的参数符合下列条件就返回真
iscntrl任何控制字符
isspace空白字符:空格‘ ’,换页‘\f’,换行'\n',回车'\r',制表符'\t',或垂直制表符'\v'
isdigit十进制数字0~9
isxdigit十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower小写字母a~z
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或数字a~z,A~Z或0~9
ispunct标点符号,任何不属于数字或字母的图像字符(可打印符号)
isgraph任何图像字符
isprint任何可打印字符,包括图像字符和空白字符

函数讲解 

这些函数的使用方法都十分类似

int islower ( int c );

如果为假,函数会返回0,如果为真,函数会返回非零的值 

iscntrl用来判断一个字符是否为控制字符

isspace用来判断一个字符是否为空白字符

isdigit用来判断字符或者字符串是否为数字

islower用来判断一个字符是否为小写字母

isupper用来判断一个字符是否为大写字母

isalpha用来判断一个字符是否为字母

isalnum用来判断一个字符是否为字母或者数字

ispunct用来判断一个字符是否为标点符号

isgraph用来判断一个字符是否为图形字符

isprint用来判断一个字符是否是可打印的

图形字符如下:

使用样例

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

int main()
{
	int i = 0;
	char str[] = "Hello World\n";
	char c;
	while (str[i])
	{
		c = str[i];
		if (islower(c))
			c -= 32;
		putchar(c);
		i++;
	}
	return 0;
}

 

这里首先要包含头文件 ctype.h

然后使用了库函数 islower用来判断字符是否为小写字母

如果字符为小写字母则将字符减去32改成大写字母

所以这个程序的作用是将字符串中所有的小写字母改成大写字母

字符转换函数

C语言提供了2个字符转换函数

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

函数讲解

tolower可以将参数传进去的大写字母转小写

toupper可以将参数传进去的小写字母转大写

使用样例

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

int main()
{
	int i = 0;
	char str[] = "Hello World\n";
	char c;
	while (str[i])
	{
		c = str[i];
		if (islower(c))
			c = toupper(c);
		putchar(c);
		i++;
	}
	return 0;
}

与上面近乎一致,只是将c -= 32变成了c = toupper(c)

两者的作用都相同,所以最终的结果也是一致的

strlen的使用和模拟实现

strlen的使用

该函数的作用是可以计算一个字符串的长度

也可以理解为计算'\0'之前出现的字符个数(不包含'\0')

这个长度最终以size_t函数的返回值形式出现(size_t为无符号整型)

我们只需要传一个字符串的起始地址给它即可(不一定是起始地址,从哪里传就从哪里开始计算)

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

int main()
{
	char str[] = "Hello World";
	int ret = strlen(str);
	printf("%d\n", ret);
	return 0;
}

答案是由10个大小写字母和1个空白字符组成,所以答案是11

strlen的模拟实现

strlen的模拟实现有三种方法

计数器

#include <stdio.h>

size_t my_strlen(const char* str)
{
	int count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

int main()
{
	char arr[] = "abcdefg";
	int ret = my_strlen(arr);
	printf("%d\n", ret);
	return 0;
}

注:如果有str为什么可以str++的问题可以看我前面的博客中的const修饰变量

C语言:指针的基础详解-CSDN博客

这里使用了1个变量count计数,随着str的增加count一同增加,直到碰到'\0'就停止即可

递归(不创建临时变量)

#include <stdio.h>

size_t my_strlen(const char* str)
{
	if (*str == '\0')
		return 0;
	else
		return 1 + my_strlen(str + 1);
}

int main()
{
	char arr[] = "abcdefg";
	int ret = my_strlen(arr);
	printf("%d\n", ret);
	return 0;
}

这里使用递归逐渐增加str的位置,并且每递归一次就增加1,最终返回这个值即可

这个方法可以不需要创建临时变量

指针-指针

这里需要有一个知识点

两个同样类型的指针相减会得出它们之间元素的个数,以此得出第三个方法

#include <stdio.h>

size_t my_strlen(const char* s)
{
	char* p = s;
	while (*p != '\0')
		p++;
	return p - s;
}

int main()
{
	char arr[] = "abcdefg";
	int ret = my_strlen(arr);
	printf("%d\n", ret);
	return 0;
}

多创建一个指针变量p让它走到'\0'之前,最后指针-指针即可

strcpy的使用和模拟实现

strcpy的使用

该函数的作用是可以将一个字符串的内容拷贝到另一个字符串中,以需要拷贝的字符串中的'\0'作为标志结束

注意:目标空间必需要足够大且可修改,不然会越界

strcpy的模拟实现

#include <stdio.h>

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

int main()
{
	char arr1[] = "abcdefg";
	char arr2[20];
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

这里的模拟实现先是创建了一个 char* ret用来保存原来目标字符串的起始地址(因为后面dest会改变),这样为后来的返回值做准备

这里直接简写成了用一个循环判断即可,当src为'\0'的时候赋值给dest自然就会停下循环

strcat的使用和模拟实现

strcat的使用

该函数的作用是可以把一个字符串连接到另一个字符串上面,也是以'\0'为标志结束

注意:目标空间必需要足够大且可修改,不然会越界

strcat的模拟实现

#include <stdio.h>

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

int main()
{
	char arr1[20] = "Hello ";
	char arr2[] = "World";
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

这里的模拟实现先是创建了一个 char* ret用来保存原来目标字符串的起始地址(因为后面dest会改变),这样为后来的返回值做准备

第一个循环的作用是找到dest的第一个'\0'

第二个循环即类似strcpy拷贝到末尾即可

最后返回起始dest的地址

strcmp的使用和模拟实现

strcmp的使用

该函数的作用是对两个字符串比较大小,从起始地址开始直到碰到'\0'结束一 一比较

如果直到结束两个字符都是相同的那么就说明字符串相等,则返回0

如果str1大于str2,返回大于0的数字,str1小于str2,返回小于0的数字(具体什么数字不做要求)

strcmp的模拟实现

#include <stdio.h>

int my_strcmp(const char* str1, const char* str2)
{
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	if (*str1 > *str2)
		return 1;
	else
		return -1;
}

int main()
{
	char arr1[10] = "abcde";
	char arr2[10] = "abq";
	int ret = my_strcmp(arr1, arr2);
	printf("%d", ret);
	return 0;
}

一个循环遍历两个字符串

如果走到'\0'则结束返回0

如果循环条件结束,则判断谁的ASCLL码值大,返回1或-1

strstr的使用和模拟实现

strstr的使用

该函数会在str1中寻找字符串str2

如果找不到则返回null

如果找到了会返回找到位置的起始地址

strstr的模拟实现

#include <stdio.h>

char* my_strstr(const char* str1, const char* str2)
{
	char* ptr1 = str1;
	char* ptr2 = str2;
	char* cur = str1;

	while (*cur != '\0')
	{
		ptr1 = cur;
		ptr2 = str2;
		while (*ptr1 == *ptr2 && cur != '\0' && ptr2 != '\0')
		{
			ptr1++;
			ptr2++;
		}
		if (*ptr2 == '\0')
			return cur;
		cur++;
	}
	return NULL;
}

int main()
{
	char arr1[20] = "hello@world.c";
	char arr2[20] = "llo";
	char* ret = my_strstr(arr1, arr2);
	printf("%s", ret);
	return 0;
}

这里需要定义几个char* 类型的变量来代替str1和str2移动

因为我们如果只有str1和str2就算能够找到str1中的str2也无法后退返回原来的地址了

 例如:

如果我们从str1中找str2的时候,发现了第一个l,那么往后走发现第二个不是o的时候,我们如何返回呢?

str2判断完str1的ll不是lo的时候str2又怎么返回到str2的起始地址l中呢?

所以这就是需要定义多个char* 类型的变量来走的原因

第一个循环的cur是用来遍历的

第二个循环是用来判断字符串是否相同的 

最后都返回找到的起始位置或者NULL即可

strncpy的使用和模拟实现

strncpy和strcpy的区别就差了一个n

这类型的函数有很多,都是加了一个n

这类函数会更加安全,因为strcpy拷贝的时候如果没有控制好数组的大小就会有越界的风险

所以strncpy的安全性会更高 

strncpy的使用

strncpy可以自己选择需要拷贝多少个数据,我们只需要传参的时候多传一个num即可 

这个num的单位是字节

strncpy的模拟实现

#include <stdio.h>

char* my_strncpy(char* dest, const char* str, size_t num)
{
	char* ret = dest;
	int i = 0;
	for (int i = 0; i < num; i++)
	{
		*(dest + i) = *(str + i);
	}
	return ret;
}

int main()
{
	char arr1[] = "abcdefg";
	char arr2[20] = { 0 };
	my_strncpy(arr2, arr1 + 1, 5);
	printf("%s", arr2);
	return 0;
}

这里的实现和strcpy差不多

只不过用了个for循环来控制需要拷贝几个字符串即可

strncat的使用和模拟实现

strncat的使用

strcat是连接字符串

那么strncat就是可以决定需要连接几个字符串

strncat的模拟实现

#include <stdio.h>

char* my_strncat(char* dest, char* str, size_t num)
{
	char* ret = dest;
	while (*dest != '\0')
	{
		dest++;
	}
	for (int i = 0; i < num; i++)
	{
		*(dest++) = *(str++);
	}
	return ret;
}

int main()
{
	char arr1[20] = "hello";
	char arr2[20] = " world";
	my_strncat(arr1, arr2 + 1, 3);
	printf("%s", arr1);
	return 0;
}

具体实现也和strcat差不多

for循环控制连接字符串个数

strncmp的使用和模拟实现

strncmp的使用

strcmp是比较两个字符串

strncmp是比较指定个数的两个字符串

其他的基本一致

strncmp的模拟实现

#include <stdio.h>

int my_strncmp(const char* str1, const char* str2, size_t num)
{
	for (int i = 0; i < num; i++)
	{
		if (*str1 > *str2)
			return 1;
		if (*str1 < *str2)
			return -1;
		str1++;
		str2++;
	}
	return 0;
}

int main()
{
	char arr1[10] = "abcde";
	char arr2[10] = "abq";
	int ret = my_strncmp(arr1, arr2, 3);
	printf("%d", ret);
	return 0;
}

用for循环控制比较的字符个数

其他的与strcmp差不多一致

memcpy的使用和模拟实现

该函数需要头文件和前面的一样 <string.h>

memcpy的使用 

我们学会了字符串的拷贝方法,但是如果我们要拷贝的是一个整形数组,或者浮点型数组,怎么办呢?

这时候就需要我们的memcpy上场了

它是可以将内存的内容拷贝走,并不是像strcpy一样只针对字符串,它针对所有人

该函数的返回值为一个void*的指针

第一个参数是要拷贝的目的地

第二个参数是要拷贝内容的地方

第三个参数是一个数字,表示拷贝内容的大小,单位是字节

那么来看看下面这串代码:

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

int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr1, sizeof(int) * 10);
	for (int i = 0; i < 10; i++)
		printf("%d ", arr2[i]);
	return 0;
}

 这串代码是把整形数组arr1的内容全部拷贝到arr2中

最终打印结果如下:

memcpy的模拟实现 

void* my_memcpy(void* dest, const void* source, size_t num)
{
	void* ret = dest;
	for (int i = 0; i < num; i++)
	{
		*(char*)dest = *(char*)source;
		(char*)dest += 1;
		(char*)source += 1;
	}
	return ret;
}

 这里定义了一个void* ret的指针变量用来保存目标的数组的第一个下标的地址,防止后续使用dest++后找不到该位置

然后用了一个for循环将source里的一个个字节的内容拷贝到dest中即可

char*就是一个字节,*(char*)就是该字节的内容

再让dest和source往后走一个字节即可

走一个字节也是强制类型转换为char*再+1即可走一个字节

memmove的使用和模拟实现

memmove的使用

memmove的使用和momcpy的使用基本上是一模一样的

刚刚那串代码改一下函数一样能使用

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

int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	memmove(arr2, arr1, sizeof(int) * 10);
	for (int i = 0; i < 10; i++)
		printf("%d ", arr2[i]);
	return 0;
}

但是他们还是有区别的,具体区别请看下文 

memmove的模拟实现

void* my_memmove(void* dest, const void* source, size_t num)
{
	void* ret = dest;
	if (dest < source)
	{
		while (num--)
		{
			*(char*)dest = *(char*)source;
			(char*)dest += 1;
			(char*)source += 1;
		}
	}
	else
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)source + num);
		}
	}
	return ret;
}

 该函数的实现和momcpy有些地方也很像

但是多了一个if语句的判断和一个while循环

if语句是判断两者谁的地址大

然后第一个while循环是从前往后拷贝内存,第二个while循环是从后往前拷贝内存

所以if语句来决定while是该从前往后还是从后往前拷贝

为什么要这么做呢?

memcpy和memmove的区别

memcpy和memmove的本质区别其实就在于当dest和source重叠的时候

当我们的函数的参数是这样的时候

memcpy(arr1 + 2, arr1, sizeof(int) * 5);

在一些编译器下 ,memcpy的拷贝是会出问题的,如果string.h代码里的memcpy函数是类似上面的my_memcpy函数那样实现的话

如果要拷贝的dest与source重叠时,只是从前往后拷贝或者从后往前拷贝就会出现问题

arr1 = 1,2,3,4,5,6,7,8,9,10

若执行上述代码后

arr1 = 1,2,1,2,1,2,1,8,9,10

就是因为在从前往后拷贝的时候把后面即将要开始拷贝的内容给覆盖了,就出现重复的1,2

这时候就需要memmove出场的时候了

在上面的my_memmove就很好的解决了内存重叠的问题,可以分情况选择从前往后拷贝还是从后往前拷贝

总结:memcpy和memmove的区别在当有dest和source有内存重叠的时候,memcpy的拷贝可能会出问题,但memmove不会


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ragef

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

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

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

打赏作者

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

抵扣说明:

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

余额充值