C语言内存函数(21)

文章目录


前言

正文开始,发车!


一、memcpy的使用和模拟实现

函数模型:void* memcpy(void* destination, const void* source, size_t num);
使用注意事项:

1.函数 memcpy 从 source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置

2.这个函数在遇到 '\0' 的时候并不会停下来,给多少就复制多少

3.如果 source 和 destination 有任何的重叠,复制的结果都是未定义

c 复制代码
// 使用举例
int main()  
{  
    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };  
    int arr2[10] = {0};  
    //将arr1中的1 2 3 4 5,拷贝到arr2中  
    memcpy(arr2, arr1, 5*sizeof(int));  
    int i = 0;  
    for (i = 0; i < 10; i++)  
    {  
        printf("%d ", arr2[i]);  // 1 2 3 4 5 0 0 0 0 0
    }  
    return 0;  
}

我们来模拟实现一下 my_memcpy ,首先所接收的 dest 、src 都是不明确类型的,用 void* 接收,返回类型为 void*,且 src 并不希望被修改,可以加个 const 修饰

其实没那么复杂,我们有 num 个字节需要复制,那么直接循环 num 次就可以了,只是需要把 dest 和 src 强制转化成 char* 的类型就OK了

标红是因为什么原因?

原来,强制类型转化是临时的,下面两句各自加上就好了

dest = (char*)dest + 1;

src = (char*)src + 1;

这时候,我们再来回想一下为什么尽量要避免 dest 和 src 两者发生重叠

你脑海里想象一下 my_memcpy(arr1 + 2, arr1 + 1, 5 * sizeof(int)); 的过程,就明白了

下面是memcpy的完整模拟实现:

c 复制代码
void* my_memcpy (void * dst, const void * src, size_t count )  
{  
    void * ret = dst;  
    assert(dst);  
    assert(src);  
    /*  
     * copy from lower addresses to higher addresses  
     */  
    while (count--) {  
        *(char *)dst = *(char *)src;  
        dst = (char *)dst + 1;  
        src = (char *)src + 1;  
    }  
    return(ret);  
}

二、memmove的使用和模拟实现

那怎么处理内存重叠的问题呢,那就用这个!

函数原型:void* memmove(void* destination, const void* source, size_t num);
使用注意事项:

1.可以把 memmove 当作可以处理重叠的 memcpy 吗? 可以!

我们来看看这个 memmove 为什么可以解决内存重叠情况下的复制:

当 src 在 dest 左边且发生重叠的时候,这时候如果从左往右复制,dest 所指向的 3 立马就被覆盖,等到 src 来复制的时候,已经只能复制 1 了,这不是我们想要的,于是,我们考虑从右向左复制,也就是 dest 的 7 被 5 覆盖 , 6 被 4 覆盖 ... 3 被 1 覆盖

当 src 在 dest 右边且发生重叠的时候,这时候从右往左复制,又会发生上述的提前覆盖的情况,解决方法是什么?从左向右复制!

当 src 与 dest 不发生重叠的时候,从左向右 或者 从右向左 复制都没影响,所以我们想出一个总的复制方案

当 src < dest 的时候,从右向左复制

当 dest < src 的时候,从左向右复制

我们来思考一下 从右往左 复制该怎么实现,首先 dest 和 src 先跳到未部,注意要减一个1

dest = (char*)dest + num - 1;

src = (char*)src + num - 1;

接着开始往回复制,这与从左向右几乎等同,无非就是自加变为自减,所以,完整的 my_memmove 如下

c 复制代码
void* my_memmove(void* dest, const void* src, size_t num)
{
	void* ret = dest;

	if (dest < src) {
		// 从左往右
		while (num--) {
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else {
		// 从右往左
		dest = (char*)dest + num - 1;
		src = (char*)src + num - 1;

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

三、memset函数的使用

函数原型:void* memset(void* ptr, int value, size_t num);

memset是用来设置内存的,将内存中的值以字节为单位设置成想要的内容

c 复制代码
// 使用实例
#include <stdio.h>  
#include <string.h>  
int main ()  
{  
    char str[] = "hello world";  
    memset (str,'x',6);  
    printf(str);  // xxxxxxworld
    return 0;  
}

请注意!memset在设置的时候,是以字节为单位来设置的

c 复制代码
// 错误使用举例
int main()  
{  
    int arr[10] = { 0 };  
    memset(arr, 1, 40);  //err
    return 0;  
}

因为它是以字节为单位来设置的,所以你可以想象,一个 int 有四个字节,每个字节都是1

也就是说,上述数组 arr 的每个元素都是 0x01010101,而不是我们想要的0x00000001

我们一般拿来清空、初始化数组或者结构体

四、memcmp函数的使用

函数原型:int memcmp(const void* ptr1, const void* ptr2, size_t num);

作用是比较从 ptr1 和 ptr2 指针指向的位置开始,向后的 num 个字节

c 复制代码
// 使用实例
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[] = { 1,2,3,4,8 };

	int ret = memcmp(arr1, arr2, 16); // 0
	printf("%d\n", ret);

	ret = memcmp(arr1, arr2, 17); // -1
	printf("%d\n", ret);

	return 0;
}

至于原理,请调试并打开内存查看,就很清楚了


总结

我们在学习的时候应该带有自己的思考,比如上述 arr1 和 arr2 的图片可能有一个地方会引起你的注意

对于每个 int 的四个字节,数据低位同时也是存放在地址的低位

举例来说,假如把 int a = 1;存放在内存里面:

0x00000093F16FF6A8 :存放01

0x00000093F16FF6A9 :存放00

0x00000093F16FF6AA :存放00

0x00000093F16FF6AB :存放00

你可能跟我一样,对数据在内存中的存储由此产生了极大的疑问和兴趣,没关系,我们下篇文章开始介绍

相关推荐
DdddJMs__1351 小时前
C语言 | Leetcode题解之第403题青蛙过河
c语言·数据结构·算法
小林熬夜学编程1 小时前
【Linux系统编程】第二十弹---进程优先级 && 命令行参数 && 环境变量
linux·运维·服务器·c语言·开发语言·算法
六点半8882 小时前
【C/C++】涉及string类的经典OJ编程题
c语言·开发语言·c++·算法
爱是小小的癌2 小时前
C语言动态内存管理
c语言·开发语言
暮色_年华2 小时前
C和指针:指针
c语言
小周的C语言学习笔记3 小时前
鹏哥C语言40---函数参数与函数调用
c语言·数据结构·算法
CV金科3 小时前
蓝桥杯-STM32G431RBT6(串口)
c语言·stm32·单片机·嵌入式硬件·蓝桥杯
破刺不会编程3 小时前
【C++】透析string类
c语言·开发语言·数据结构·c++
毅凉5 小时前
Linux笔记
linux·c语言·网络·数据库
CV金科5 小时前
蓝桥杯-STM32G431RBT6(UART解析字符串sscanf和解决串口BUG)
c语言·stm32·单片机·嵌入式硬件·mcu·算法·bug