实现一个Memcpy函数

memcpy函数解析与实践
本文详细介绍了C/C++中memcpy函数的功能与使用方法,并通过示例代码演示了如何处理内存重叠的情况。此外还深入探讨了void指针的特性和用途。

首先我们必须明白memcpy函数在函数库里面的功能:

函数简介:

c和c++使用的内存拷贝函数,memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。

另外这里要考虑内存重叠的情况。

另外因为题中没有指定类型,一部人可能无从下手,这里使用void指针,这里介绍下:

void的含义
void即“无类型”,void *则为“无类型指针”,可以指向任何数据类型。

void指针使用规范
void指针可以指向任意类型的数据,亦即可用任意数据类型的指针对void指针赋值。例如:
int *pint;
void *pvoid;
pvoid = pint; /* 不过不能 pint = pvoid; */
如果要将pvoid赋给其他类型指针,则需要强制类型转换如:pint = (int *)pvoid;

在ANSI C标准中,不允许对void指针进行算术运算如pvoid++或pvoid+=1等,而在GNU中则允许,因为在缺省情况下,GNU认为void *与char *一样。sizeof( *pvoid )== sizeof( char ).

void的作用
对函数返回的限定。
对函数参数的限定。
当函数不需要返回值时,必须使用void限定。例如: void func(int, int);
当函数不允许接受参数时,必须使用void限定。例如: int func(void)。


由于void指针可以指向任意类型的数据,亦即可用任意数据类型的指针对void指针赋值,因此还可以用void指针来作为函数形参,这样函数就可以接受任意数据类型的指针作为参数。例如:
void * memcpy( void *dest, const void *src, size_t len );
void * memset( void * buffer, int c, size_t num );

另外提一下自己敲得代码,以及测试主函数:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<map>
#include<set>
#include<cctype>
#include<algorithm>
using namespace std;
void *Memcpy(void *dest,const void *src,size_t count)
{
    if(dest==NULL||src==NULL)
        return NULL;
    char *pdest=static_cast<char*>(dest);
    const  char *psrc=static_cast<const char*>(src);
    int n=count;
    if(dest>=src+count||src>=dest+count){
           for(size_t i=0;i<n;i++)
            pdest[i]=psrc[i];
    }
    else
    {
        //内存重叠
        //被复制在复制的后面,高位开始复制
        if(pdest>psrc)
        {
            for(size_t i=n-1;i!=-1;--i)
            {
                pdest[i]=psrc[i];
            }
        }
        else
        {
            //被复制在复制的前面,地位开始复制
            for(size_t i=0;i<n;i++)
            {
                pdest[i]=psrc[i];
            }
        }
    }
    return pdest;
}
int main()
{
	int a[100],b[100],n;
	cin>>n;
	for(int i=0;i<n;i++)
        cin>>a[i];
    Memcpy(b,a,sizeof(int)*n);
    for(int i=0;i<n;i++)
        cout<<b[i]<<' ';
    cout<<endl;
	return 0;
}


<think> 我们参考引用资料来实现一个memcpy函数。根据引用[1]和引用[3],memcpy函数的功能是将源内存区域的内容复制到目标内存区域,复制长度为指定的字节数。需要注意的是,标准库中的memcpy通常要求源内存区域和目标内存区域不重叠,如果重叠则行为未定义。但在引用[2]中,我们看到了memmove函数可以处理重叠区域,因此如果用户需要处理重叠区域,应该使用memmove。不过,根据题目要求,我们只实现memcpy,且不考虑重叠区域(即假设源和目标不重叠)。 函数原型:`void* my_memcpy(void* dest, const void* src, size_t num);` 其中: dest: 目标内存地址 src: 源内存地址(const表示源内容不会被修改) num: 要复制的字节数 实现思路: 1. 由于不知道具体的数据类型,我们需要逐字节复制。因此,可以将void*指针强制转换为char*指针,因为char类型占1个字节。 2. 循环复制num个字节,每次复制一个字节。 3. 返回目标内存地址dest。 但是,引用[4]提到:强制类型转换是一次性的,所以每次移动指针时都需要进行转换。我们可以先将dest和src指针都转换为char*,然后用循环逐个字节复制。 注意:我们假设源和目标内存区域不重叠。如果重叠,则应该使用memmove。 实现代码: ```c #include <stdio.h> #include <assert.h> void* my_memcpy(void* dest, const void* src, size_t num) { // 保存目标地址的起始位置,因为后面dest会移动 void* ret = dest; // 断言,确保dest和src不为空指针(实际库函数可能没有断言,但这里为了安全) assert(dest != NULL && src != NULL); // 转换为char*指针,进行逐字节复制 char* d = (char*)dest; const char* s = (const char*)src; // 循环复制num个字节 while (num--) { *d++ = *s++; } return ret; } int main() { int arr1[] = {1,2,3,4,5,6,7,8,9,10}; int arr2[10] = {0}; // 复制arr1的前5个整数到arr2 my_memcpy(arr2, arr1, 5 * sizeof(int)); // 打印arr2的内容 for (int i = 0; i < 10; i++) { printf("%d ", arr2[i]); } printf("\n"); return 0; } ``` 上述代码中,我们使用`char*`指针逐个字节复制。注意,我们使用了`const char*`来指向源地址,因为我们不会修改源数据。 另外,我们添加了断言(assert)来确保传入的指针非空。在实际库函数中,可能不会进行这样的检查,但为了安全,我们可以加上。注意,使用assert需要包含头文件`<assert.h>`。 运行main函数,arr2的前5个元素应该变成1,2,3,4,5,后面5个元素仍然是0。 注意:如果目标区域和源区域有重叠,使用这个函数可能会出现未定义行为(因为复制过程中源数据可能被覆盖)。例如,如果想将arr1的前5个整数复制到arr1+2的位置(即从下标0开始的5个数复制到下标2开始的位置),那么就会发生重叠,此时应该使用memmove(如引用[2]所示)。 因此,在调用memcpy时,必须确保源和目标内存块不重叠。 §§相关问题§§ 1. 如何实现一个可以处理内存重叠的复制函数(即memmove)? 2. 除了memcpy,C语言中还有哪些内存操作函数? 3. 为什么不直接使用赋值操作而需要内存复制函数? </think> ### 使用C语言实现memcpy函数 `memcpy`函数用于将指定字节数的数据从源内存地址复制到目标内存地址,不处理内存重叠区域(需用`memmove`处理重叠)。以下是基于引用实现的完整代码和解析: ```c #include <stdio.h> #include <assert.h> // 自定义memcpy实现 void* my_memcpy(void* dest, const void* src, size_t num) { // 保存目标地址起始位置作为返回值 void* ret = dest; // 安全检查:禁止空指针 assert(dest && src); // 逐字节复制(兼容任意数据类型) char* d = (char*)dest; // 强制转换为char*进行字节操作 const char* s = (const char*)src; while (num--) { *d++ = *s++; // 复制后指针自增 } return ret; } int main() { int arr1[] = {1,2,3,4,5,6,7,8,9,10}; int arr2[10] = {0}; // 测试:复制5个整型数据(20字节) my_memcpy(arr2, arr1, 5 * sizeof(int)); // 验证结果 for(int i=0; i<10; i++) { printf("%d ", arr2[i]); // 输出:1 2 3 4 5 0 0 0 0 0 } return 0; } ``` #### 关键实现解析: 1. **类型转换** - 使用 `(char*)` 强制转换实现逐字节复制,适配所有数据类型[^4] - `const void* src` 确保源数据不被修改[^3] 2. **指针操作** ```c *d++ = *s++; // 等效于: // *d = *s; d += 1; s += 1; ``` 通过指针自增实现连续内存复制,避免反复强制转换[^4] 3. **安全措施** - `assert(dest && src)` 防止空指针导致崩溃[^1] - 返回 `dest` 起始地址符合标准库规范[^3] 4. **注意事项** - ❗**不处理内存重叠**(如 `my_memcpy(arr1+2, arr1, 20)` 会导致数据错误) - 实际库函数可能使用汇编优化速度(此实现侧重原理演示) #### 典型应用场景: - 结构体复制:`my_memcpy(&obj2, &obj1, sizeof(MyStruct))` - 数组片段复制(如示例中的整型数组) - 网络数据传输前的内存准备 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值