• 【C语言】内存函数的详细教学和模拟实现


    🚀write in front🚀
    🔎大家好,我是gugugu。希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎
    🆔本文由 gugugu 原创 CSDN首发🐒 如需转载还请通知⚠
    📝个人主页gugugu—精品博客
    🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​
    📣系列专栏:gugugu的精品博客
    ✉️我们并非登上我们所选择的舞台,演出并非我们所选择的剧本📩

    在这里插入图片描述
    vs 启动

    前言

    上一篇博客里讲到了字符函数和字符串函数,那么在这一篇博客中,我们将另一种常见的函数讲解一下,就是内存函数,内存函数比字符函数和字符串函数更加的广泛,毕竟是针对内存的函数。
    在这里插入图片描述

    一、内存函数与字符串函数的区别

    C语言内存函数,是针对内存块的,不在乎内存中的数据,但是字符串函数时针对字符串的,在乎内存中的数据,只操作字符串,与\0操作符关系密切。

    二、memcpy函数

    memcpy函数与strcpy函数功能比较相似,都是进行拷贝操作,但是memcpy针对的对象不同。

    1、memcpy函数的基本结构

    void* memcpy(void * destination ,const void * source,size_t num);
    函数有三个参数,分别为起始地址,目标地址和移动的字节的大小,返回值是void*

    • 那么为什么起始地址和目标地址,以及返回值都是void类型呢?

    在这里插入图片描述

    因为memcpy函数针对的对象是内存空间,而内存空间中储存的数据类型不清楚,有多种可能性,所以直接使用void*类型的指针,在使用时,进行强制类型转换。

    另外,在这里补充一点
    在上一篇文章里面,很多字符串函数的返回值都是一个指针,这是为什么呢?

    在这里插入图片描述

    其实,这是为了能够通过返回值去更方便的进行链式访问

    2、memcpy函数的模拟实现

    在模拟实现memcpy这些内存函数的时候,主要是要注意对void*的强转,这比较巧妙。

    这里提供两种方法。大同小异
    方法一

    #include 
    #include 
    
    void* my_memcpy(void* ch1, const void* ch2, size_t num)
    {
        assert(ch1 && ch2);
        void* ret = ch1;
        int i = 0;
        for (i = 0; i < num; i++)
        {
            *((char*)ch1)++ = *((char*)ch2)++;
        }
        return ret;
    }
    
    
    int main()
    {
        int arr1[10] = { 0 };
        int arr2[] = { 1,2,3,4,5,6,7,8 };
    void * ret1=my_memcpy(arr1, arr2, 20);
        int i = 0;
        for (i = 0; i < 10; i++)
        {
            printf("%d ", *(((int*)ret1)++));
        }
        printf("\n");
    
        char ch1[] = "ZZZZZZZZZZZ";
        char ch2[] = "YYYYYYYYY";
        void * ret2=my_memcpy(ch1, ch2, 6);
        printf("%s\n", (char *)ret2);
        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
    • 32
    • 33
    • 34

    方法二

    #include 
    #include 
    
    void* my_memcpy(void* ch1, const void* ch2, size_t num)
    {
        assert(ch1 && ch2);
        void* ret = ch1;
        while (num--)
        {
            *(char*)ch1 = *(char*)ch2;
            ch1 = (char*)ch1 + 1;
            ch2 = (char*)ch2 + 1;
        }
        return ret;
    }
    
    
    int main()
    {
        int arr1[10] = { 0 };
        int arr2[] = { 1,2,3,4,5,6,7,8 };
    void * ret1=my_memcpy(arr1, arr2, 20);
        int i = 0;
        for (i = 0; i < 10; i++)
        {
            printf("%d ", *(((int*)ret1)++));
        }
        printf("\n");
    
        char ch1[] = "ZZZZZZZZZZZ";
        char ch2[] = "YYYYYYYYY";
        void * ret2=my_memcpy(ch1, ch2, 6);
        printf("%s\n", (char *)ret2);
        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
    • 32
    • 33
    • 34
    • 35

    memcpy函数针对的对象是内存空间,所以对整形和字符都可以处理

    三、memmove函数

    1、memmove函数的优势

    memcpy函数在使用时会存在问题,比如目标空间和起始空间发生了重叠,此时使用memcpy函数就会出现问题。
    看下面的例子
    在这里插入图片描述

    #include 
    #include 
    
    void* my_memcpy(void* ch1, const void* ch2, size_t num)
    {
        assert(ch1 && ch2);
        void* ret = ch1;
        while (num--)
        {
            *(char*)ch1 = *(char*)ch2;
            ch1 = (char*)ch1 + 1;
            ch2 = (char*)ch2 + 1;
        }
        return ret;
    }
    
    int main()
    {
        int arr[10] = { 1,2,3,4,5,6,7 };
        my_memcpy(arr + 2, arr, 20);
        int i = 0;
        for (i = 0; i < 10; i++)
        {
            printf("%d ", arr[i]);
        }
        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

    按照设想,答案应该是 1 2 1 2 3 4 5 0 0 0
    但是实际答案是
    在这里插入图片描述
    是不是没想到?
    在这里插入图片描述

    这是为什么呢?
    主要是在实现的时候,读取到第三个数的时候,本来是3,但是被赋值之后就变成了1,所以第三个数也就成了1,而不是三,后面也是一样。

    但是memmove函数可以解决这个问题
    在这里插入图片描述

    2、memmove函数的模拟实现

    像上面实现memcpy一样从前面向后面拷贝出现了问题,那么如果从后面往前面拷贝,又当如何?
    在这里插入图片描述

    这是就会先将5放到arr[6]上,4放到arr[5]上,依次类推,可以发现,不会出现问题。

    但是又有新的问题,如果是memmove(arr,arr+2,20),这又会怎么办呢?

    这是从后往前就不行了,就得从前往后拷贝。

    聪明的小伙伴,看到这里肯定能够想出解决方案。

    • 当目的地址比起始地址大时,从后往前拷贝
    • 当目的地址比起始地址小时,从前往后拷贝

    上代码
    在这里插入图片描述

    #include 
    #include 
    void* my_memmove(void* ch1, const void* ch2, size_t num)
    {
        assert(ch1 && ch2);
        void* ret = ch1;
        if (ch1 > ch2)
        {
            while (num--)//自减操作后num已经是19了
            {
                *((char*)ch1 + num) = *((char*)ch2 + num);//每次自减操作后,num都会少1,向前走了一个字节
            }
        }
        else
        {
            while (num--)
            {
                *(char*)ch1 = *(char*)ch2;
                ch1 = (char*)ch1 + 1;
                ch2 = (char*)ch2 + 1;
            }
        }
        return ret;
    }
    
    int main()
    {
        int arr1[10] = { 0 };
        int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };
        my_memmove(arr2, arr2+2, 20);
        int i = 0;
        for (i = 0; i < 10; i++)
        {
            printf("%d ", arr2[i]);
        }
        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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    四、memset函数

    这个函数比较简单,就不详细讲解了
    在这里插入图片描述

    1、memset函数的功能

    set的意思是设置,我们在这里把它理解成赋值,就是给内存去赋值

    先写段代码看看功能吧

    #include 
    
    int main()
    {
    	char ch[20] = "hello world!";
    	memset(ch, 'x', 10);
    	printf("%s\n", ch);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    需要注意的是

    memset函数的结构比较特殊
    void * memset(void* ptr,int value,size_t num);
    第二个参数是int类型,为啥我的例子里面给的是char呢?

    是因为char是使用ASCII码值进行操作的。

    2、memset函数的模拟实现

    比较简单,直接上代码

    #include 
    #include 
    void* my_memset(void* ch, int value ,size_t num)
    {
    	assert(ch);
    	void* ret = ch;
    	while (num--)
    	{
    		*(char*)ch = value;
    		ch = (char*)ch + 1;
    	}
    	return ret;
     }
    int main()
    {
    	char ch[20] = "hello world!";
    	my_memset(ch, 'x', 10);
    	printf("%s\n", ch);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述
    运行成功,yeah

    五、memcmp函数

    这个函数也比较简单,就是对内存进行比较

    1、memcmp函数的基本结构

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

    • 返回值是int 跟strcmp一样
    • 两个指针参数都加上了const ,无法修改内容
    • num是比较的字节数

    2、memcmp函数的模拟实现

    比较简单,直接上代码
    在这里插入图片描述

    #include 
    #include 
    int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
    {
    	assert(ptr1 && ptr2);
    	while (num--)
    	{
    		if (*(char*)ptr1 == *(char*)ptr2)
    		{
    			ptr1 = (char*)ptr1 + 1;
    			ptr2 = (char*)ptr2 + 1;
    		}
    		else
    			return *(char*)ptr1 - *(char*)ptr2;
    	}
    	return 0;
    }
    int main()
    {
    	char ch1[20] = { 0 };
    	char ch2[20] = { 0 };
    	gets(ch1);
    	gets(ch2);
    	int num = 0;
    	scanf("%d", &num);
    	int ret = my_memcmp(ch1, ch2, 5);
    	if (ret > 0)
    		printf(">\n");
    	else if (ret < 0)
    		printf("<\n");
    	else
    		printf("==\n");
    	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
    • 32
    • 33
    • 34


    ok ,这次的分享到这里就结束了,函数的内容基本上就要告一段落了

    今天下午还会有一更哦,敬请关注!!!


    !!!!!!!!!!!!!!!!!求关注!!!!!!!!!!!!!!!!

    !!!!!!!!!!!!!!!蹲个一键三连!!!!!!!!!!!!!!!

    在这里插入图片描述

  • 相关阅读:
    2022-随便学学
    Solidworks 发生一个或多个重建错误却不显示出来
    力扣:120. 三角形最小路径和(Python3)
    JavaMail连接Office 365使用XOAUTH2身份认证
    Servlet的url-pattern配置
    编写playbook实现LNMP架构基于源码方式、变量,加密文件
    Mysql 事务和存储引擎的概念
    MyBatis中的$和#,你知道他们的区别吗?
    iOS报错命名空间“std”中的“unary_function”
    C语言----数组
  • 原文地址:https://blog.csdn.net/2303_78940834/article/details/133547071