• 模拟实现memcpy memmove,字符类库函数的介绍,strerror,strtok的使用讲解。


    strtok

    在这里插入图片描述
    上图是关于strtok库函数的介绍,很多小伙伴一看就头疼了,下面让我来给大家讲解一下这个库函数的作用。这个库函数的作用是分割字符串,首先我们来看函数的声明

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

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

    6.如果字符串中不存在更多的标记,则返回 NULL 指针

    我们在代码中使用一下这个库函数

    int main()
    {
    	char arr[] = "abcd.efg@abcd";
    	const char delimiters[] = ".@";
    	char* str = NULL;
    	char arr2[30] = { 0 };
    	strcpy(arr2, arr);
    
    	for (str = strtok(arr2, delimiters); str != NULL; str = strtok(NULL, delimiters))
    	{
    		printf("%s\n", str);
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述
    我们用一张图来让大家明白strtok是如何实现的
    在这里插入图片描述
    通过上述过程我们就实现了分割字符串的效果,这就是strtok库函数的作用。

    strerror

    我们在写代码时经常会报错,错误会有一个错误码,编译器会将错误码保存在错误码遍历中,C语言提供的错误码变量为errno,而库函数strerror的作用就是将错误码翻译成错误信息

    在这里插入图片描述
    我们可以在代码中演示一下,strerror的使用场景

    #include 
    int main()
    {
    	FILE* pf = fopen("test.txt", "r");
    
    	if (pf == NULL)
    	{
    		printf("%s\n", strerror(errno));
    		return 1;
    	}
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述
    我们还可以使用perror,同样可以打印错误信息

    在这里插入图片描述

    一些字符类库函数

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

    我们可以在编译器中简单使用几个这样的库函数

    int main()
    {
    	char ch[] = "Test String";
    	int i = 0;
    	char c = 0;
    	while (ch[i])
    	{
    		c = ch[i];
    		if (isupper(c))
    		{
    			c = tolower(c);
    		}
    		putchar(c);
    		i++;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    memcpy

    在之前的分享中我与大家分享了strcpy库函数,实现了字符串的拷贝,那么如果我想拷贝的是一个整形数组,是否有一个库函数可以实现我的需求呢?我们今天就来介绍一下memcpy库函数,并跟大家一起模拟实现一下。
    在这里插入图片描述
    其实strcpymemcpy的实现思路大致相同,只不过memcpy的参数类型变成了void*这样就可以实现不同类型数据的拷贝,并且memcpy的参数中增加了一个size_t numnum的含义是:从source开始复制num个字节的数据到destination中。好了了解了以上这些后,我们在vs中使用一下这个库函数。

    #include
    #include
    
    int main()
    {
    	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    	int arr2[10] = { 0 };
    	memcpy(arr2, arr, 20);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述
    我们通过调试可以看到,通过调用mempy库函数我们将arr数组中的5个元素拷贝到了arr2数组当中,为什么是五个元素呢?因为数组为整形数组一个元素占4个字节,我们调用memcpy时传递给num的值为20,所以需要拷贝20个字节的数据,所以拷贝了5个元素。

    模拟实现

    经过了上面的学习,我们可以尝试这自己模拟实现一下memcpy这个库函数。

    #include
    #include
    #include
    
    void* my_memcpy(void* dest, 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;
    }
    
    int main()
    {
    	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    	int arr2[10] = { 0 };
    	my_memcpy(arr2, arr, 20);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述
    因为memcpy函数的参数是void*类型,我们在使用参数时,需要将参数强制类型转换成char*类型,这样我们在拷贝数据时就可以一个字节一个字节的拷贝,当num为0时循环结束,我们就拷贝了num个字节的数据。

    memmove

    memmove库函数与memcpy库函数的主要区别就是,memmove库函数需要实现重叠内存的拷贝,而memcpy只需要实现不重叠内存的拷贝就可以了。
    在这里插入图片描述
    我们可以见memmove库函数的函数声明与memcpy库函数的声明其实是一样的,那么什么是重叠内存的拷贝呢?我们在代码中给大家举例说明。

    模拟实现

    在这里插入图片描述
    如上图这个代码,我们想将红色区域的内容拷贝到绿色区域,最后的结果将数组变成1,2,1,2,3,4,5,8,9,10
    在这里插入图片描述
    类似于这样的拷贝就是重叠内存的拷贝,如果继续使用memcpy我们会发现数组并没有变成我们想要的数组,是因为当我们在拷贝时,将数组的第三个元素变成了1,当再次使用时元素3已经被替换成了1,所以没有达到我们期望的效果。那么如何解决这样的问题呢?当我们从前向后拷贝数组元素时,前面的数组元素会在拷贝前被改变,那么我们如果从后向前拷贝这样的问题是不是就是解决了呢。所以经过分析,当dest > src时我们从后向前拷贝,其他情况我们从前向后拷贝。 我们将模拟实现的代码进行优化

    void* my_memmove(void* dest, 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;
    }
    
    int main()
    {
    	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    	my_memmove(arr + 2, arr, 20);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    在这里插入图片描述
    如图这样我们就模拟实现了库函数memmove

  • 相关阅读:
    vuepress 配置文件分类管理
    【英语:语法基础】C1.日常对话-自我介绍
    Python可视化必备,在Matplotlib/Seaborn中轻松玩转图形拼接!
    解析java中的String类中的常用方法(二)
    逆向学习汇编篇:数据的存储与读取
    信息安全和网络空间安全选哪个?
    Thinger.io 支持多协议、插件化开源 IoT 物联网平台
    如何去实现配置化页面 - 戴向天
    LOGO 8.3 Web Server功能
    10驾校科目一考试系统——窗口交互
  • 原文地址:https://blog.csdn.net/weixin_64182409/article/details/126845439