自己实现memcpy()

#include<stdio.h>
void *my_memcpy(void *dest, void *src, int num)
{
	if((dest == NULL) ||(src == NULL))
	{
		printf("parameter error!\n");
		return 0;
	}

	char *pdest;
	char *psrc;
	pdest = dest;
	psrc = src;
	while(num --)
	{
		*pdest = *psrc;
		*pdest ++;
		*psrc++;
	}
	return dest;
}
int main(int argc, char *argv[])
{
	int pdes[] = {0};
	char psrc[] = "abcdef";
	my_memcpy(pdes, psrc, sizeof(psrc));
	printf("src : %s\n", (char *)psrc);	
}
可以直接编译通过!
<think>题目要求自己实现memcpy,但遇到内存不连续的情况。通常memcpy要求源内存和目标内存不能重叠,如果重叠则应该使用memmove。但这里的问题可能是问当内存不连续时如何赋值?实际上,memcpy本身是用于连续内存块的复制。如果内存不连续,我们可能需要多次调用memcpy或者设计其他方法。 然而,根据问题,我理解可能是要处理源内存和目标内存重叠的情况(即非连续的内存地址空间?),但通常重叠的情况属于内存重叠,而不是不连续。这里可能存在歧义。 不过,让我们重新理解问题:用户可能是在问,如果源数据和目标数据在物理内存上不连续(例如,分散在多个非连续块),如何实现一个类似memcpy的功能来复制这些数据?但标准memcpy只处理连续内存块。 但根据常见的面试题,更可能的是在问:当源内存和目标内存有重叠时,如何正确复制?即实现memmove的功能。 因此,我假设问题意图是:自己实现一个内存复制函数,能够处理内存重叠的情况(即内存不连续可能是指重叠区域导致的不连续复制?)。实际上,内存重叠时,复制过程需要避免覆盖未复制的数据。 标准做法是: - 当目标地址在源地址之前(即目标地址 < 源地址),从前往后复制。 - 当目标地址在源地址之后(即目标地址 > 源地址),且两个区域有重叠时,从后往前复制,以避免覆盖源数据。 因此,我将实现一个类似memmove的函数,它可以处理内存重叠。 下面用C语言实现: ```c #include <stdio.h> #include <stddef.h> // 用于size_t void my_memmove(void *dest, const void *src, size_t n) { char *d = (char *)dest; const char *s = (const char *)src; // 如果目标地址在源地址之前,或者没有重叠,则从前往后复制 if (d < s) { for (size_t i = 0; i < n; i++) { d[i] = s[i]; } } // 如果目标地址在源地址之后,并且有重叠,则从后往前复制 else if (d > s) { for (size_t i = n; i > 0; i--) { d[i-1] = s[i-1]; } } // 如果地址相同,则什么都不做 } int main() { char str[] = "hello, world"; // 测试重叠:将"hello"复制到后面的位置,覆盖", world" my_memmove(str + 6, str, 5); printf("After overlapping move: %s\n", str); // 应该输出"hellohello world"?实际上,这里需要仔细设计测试用例 // 另一个测试:将后面的数据复制到前面 char str2[] = "hello, world"; my_memmove(str2, str2+7, 5); // 将"world"复制到前面 printf("After moving 'world' to front: %s\n", str2); // 输出"world, world" return 0; } ``` 注意:上面的实现中,当目标地址大于源地址时,我们从后往前复制,这样可以避免重叠部分被覆盖。当目标地址小于源地址时,从前往后复制是安全的。当相等时,不需要做任何操作。 但是,上述代码在测试时需要注意,因为字符串复制后需要保证有终止符,但我们的复制操作是内存操作,不关心内容,所以测试用例中字符串需要足够长以避免越界。 ### 关键点: 1. **内存重叠处理**:通过判断源地址和目标地址的相对位置,决定复制方向。 2. **安全性**:避免在复制过程中覆盖还未复制的源数据。 3. **效率**:使用循环逐个字节复制,没有使用标准库的memcpy(通常标准库有更高效的实现,但这里为了演示)。 ### 测试说明: - 第一个测试:将字符串的前5个字符("hello")复制到从第6个位置开始,结果字符串变成"hellohello world"(注意原字符串是"hello, world",复制后覆盖了逗号和空格,但后面的部分还在,因为复制了5个字符,所以第6个位置开始覆盖5个字符,即覆盖了", w",所以结果应该是"hellohelloorld"?)这需要更严谨的测试。 实际上,第一个测试中,源字符串是"hello, world",包括12个字符(不包括终止符),数组大小为13。将str开始的5个字符复制到str+6的位置,即: 复制前:str = "hello, world\0" 复制后:从str+6开始被覆盖为'h','e','l','l','o',所以字符串变成:前6个字符不变("hello,")然后变成"hello"(覆盖了原来的空格' '和'w'的前半?),实际上位置6是原来的空格(str[6]是空格),然后我们复制5个字节到str+6,即: 位置6: 'h'(原来空格被覆盖) 位置7: 'e'(原来'w'被覆盖) 位置8: 'l'(原来'o'被覆盖) 位置9: 'l'(原来'r'被覆盖) 位置10: 'o'(原来'l'被覆盖) 然后后面的'd'和'\0'还在。所以整个字符串变成"hello,hellod"?但这样不是我们想要的。实际上,我们期望的是"hellohello"后面接着剩下的部分?但这里复制后,字符串变成"hello,hellod"(因为只覆盖了5个字节,并没有覆盖后面的部分)。所以输出为"hello,hellod"。 因此,更合适的测试用例可能是: ```c char str[] = "hello, world"; // 我们希望将前5个字符复制到位置7,这样就不会覆盖源数据(但这里目标地址大于源地址,且重叠部分只有3个字符:从7开始到9,而源数据是0-4,目标地址是7-11,重叠部分为7-9(源数据的7-9)和目标地址的7-9)?实际上,这个例子中重叠并不影响,因为目标地址在源地址后面,并且重叠的区域在复制时已经被复制过了?所以应该从前往后?但我们的函数在目标地址大于源地址时从后往前复制,这样避免覆盖。 // 修改测试用例:将"world"复制到前面 char str2[] = "world, hello"; // 将str2+7开始的5个字符("hello")复制到str2的位置,这样目标地址小于源地址,且重叠。 // 但这里没有重叠,因为源地址在7,目标地址在0,且复制的长度为5,所以没有重叠。所以我们需要一个重叠的例子。 // 重叠的例子:数组中间有一段重叠 char data[20] = "abcdefghijklmnop"; // 将data+5开始的5个字符复制到data+3(目标地址小于源地址,有重叠) // 复制前:abcdefghijklmnop // 复制后:我们希望复制5个字符:将fghij复制到cde的位置,这样会变成abfghijhijklmnop?但实际我们期望的是:将data+5开始的5个字符(fghij)复制到data+3,所以: // 位置3: f(原来'd'变成'f') // 位置4: g(原来'e'变成'g') // 位置5: h(原来'f'变成'h',但注意,源数据中位置5是f,但是在复制过程中,当我们复制位置5时,源数据已经被修改了?所以需要避免覆盖。 // 因为目标地址(3)小于源地址(5),所以应该从前往后复制,这样复制过程中,源数据中未复制的部分(位置5及以后)在复制前没有被修改,所以可以正确复制。 // 复制后:data = "abfghghijklmnop" ? 但实际应该是:我们复制了5个字符,所以位置3到7是fghij,那么: // 复制后:data[0]='a', [1]='b', [2]='c', [3]='f', [4]='g', [5]='h', [6]='i', [7]='j', [8]='i'(原来位置8的i被覆盖了吗?)实际上,我们只复制了5个字符,所以位置3-7被覆盖,位置8及以后不变。所以复制后:data[0]到data[7]是"abcfgghij"?不对,因为原来位置3是'd',4是'e',5是'f',6是'g',7是'h',8是'i',9是'j'...。复制后,位置3变成'f'(源位置5),位置4变成'g'(源位置6),位置5变成'h'(源位置7),位置6变成'i'(源位置8),位置7变成'j'(源位置9)。所以结果字符串为:前两个字符"ab",然后位置2是'c'(不变),位置3开始是"fghij",然后位置8是原来的'i'(没有被覆盖?因为源数据位置8的字符在复制时被作为源数据复制到位置6,但位置8本身没有被修改,所以位置8还是'i',位置9是'j'(但被复制到位置7了)?所以实际上,位置8和9没有被修改,所以从位置5开始:位置5是'h'(原来位置7的字符),位置6是'i'(原来位置8),位置7是'j'(原来位置9),位置8还是'i'(原来的位置8,没有被覆盖),位置9是'j'(原来的位置9,没有被覆盖)。所以整个字符串变成:ab + c + fghij + ijklmnop?即"abcfghijijklmnop"。 这很复杂,所以通常我们使用memmove来处理重叠,而我们的函数设计就是按照memmove的方式。 因此,我们实现的是memmove的功能,而不是简单的memcpymemcpy不处理重叠)。 ### 总结: - 这个函数实际上是实现了memmove的功能,可以处理源内存和目标内存重叠的情况。 §§相关问题§§ 1. 在实现memmove时,为什么需要根据源地址和目标地址的相对位置来决定复制方向? 2. 如果复制的内存区域非常大,如何优化上述内存复制函数的性能? 3. 在C语言中,memcpy和memmove的主要区别是什么?请举例说明。 4. 上述实现中,当源地址和目标地址重叠且目标地址大于源地址时,从后往前复制如何避免覆盖未复制的数据?</think>### 自己实现 memcpy 处理内存不连续赋值 在 C 语言中实现 `memcpy` 处理内存不连续(重叠)的情况时,需要特别注意复制方向。标准做法是: 1. 当目标地址在源地址之后(`dest > src`)时,从后向前复制 2. 当目标地址在源地址之前(`dest < src`)时,从前向后复制 3. 当内存区域完全不重叠时,两种方向均可 ```c #include <stdio.h> #include <stddef.h> void* my_memcpy(void* dest, const void* src, size_t n) { char* d = (char*)dest; const char* s = (const char*)src; // 处理内存重叠情况 if (d > s && d < s + n) { // 目标在源之后 - 从后向前复制 for (size_t i = n; i > 0; i--) { d[i-1] = s[i-1]; } } else { // 无重叠或目标在源之前 - 从前向后复制 for (size_t i = 0; i < n; i++) { d[i] = s[i]; } } return dest; } int main() { char buffer[50] = "Hello, World!"; // 测试内存重叠情况 (目标在源之后) printf("Before: %s\n", buffer); my_memcpy(buffer + 7, buffer, 5); // 复制"Hello"到"World"位置 printf("After: %s\n", buffer); // 输出: Hello, Hello! return 0; } ``` ### 关键点说明: 1. **内存重叠检测**: ```c if (d > s && d < s + n) { ... } ``` 判断目标地址是否在源地址范围内 2. **从后向前复制**(处理目标在源之后的情况): ```c for (size_t i = n; i > 0; i--) { d[i-1] = s[i-1]; } ``` 避免覆盖未复制的源数据 3. **从前向后复制**(处理无重叠或目标在源之前): ```c for (size_t i = 0; i < n; i++) { d[i] = s[i]; } ``` 4. **返回值**:返回目标指针,支持链式调用 ### 内存重叠处理原理: 1. **目标在源之后**:从后向前复制,确保先复制高位数据 2. **目标在源之前**:从前向后复制,正常顺序即可 3. **完全不重叠**:两种方向结果相同 ### 性能考虑: 1. 实际标准库实现使用汇编优化和向量指令 2. 大块内存复制时使用字长(word-size)操作 3. 对齐优化提高内存访问效率
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值