关于memcpy拷贝函数的基本介绍及所遇的坑

博客介绍了在ZMQ通信中如何将vector<vector<Point>>转换为一维指针数组进行传递,并探讨了memcpy与memmove函数的使用及其区别。作者在实际操作中遇到了因忽视字节拷贝导致的数据混乱问题,最终通过调整memcpy参数解决了问题。文章适合对C++内存操作和ZMQ通信有一定了解的读者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 前情提要

  1. 如题,最近项目程序里使用zmq通信,原意是要将一个vector<vector<Point>>类型的数据结构从Req发送到ReP端,然后对其进行操作。但是本人试过之后发现一旦将其打包好,发送过去,另一端无法访问到内层结构及数据,(相当于传了一个黑盒过去,而自己却需要内部的数据。。。)可能需要处理把整个结构完好无损的发过去才能访问到吧。无奈本人才疏学浅,不知道要怎么打包整个结构。有幸看到这里的大佬如果有想法还请不吝赐教!!!
  2. 再说回来,不会怎么办呢,来个曲线救国,我把整个结构给它压缩成了一维:
int *rawData = new int[num];

像这样,new了一个指针数组,把所有数据都由这个指针数组来指向。然后将指针数组发送过去,实现间接访问。

2. memcpy函数

函数原型
void *memcpy(void*dest, const void *src, size_t n);
函数源码
void* MyMemcpy(void* dest,const void* src,size_t num)
{
     assert(dest&&src);
     char* dest_t=(char*)dest;//目标字符串
     const char* src_t=(const char*)src;//源字符串
     while(num--)
     {
         *dest_t++= *src_t++;
     }
     return dest;
}
//(不能解决内存重叠的问题,正序拷贝,适用于任何类型)
功能

由src指向地址为起始地址的连续n个字节的数据复制到以destin指向地址为起始地址的空间内。

说明

1.source和destin所指内存区域不能重叠,函数返回指向destin的指针。
2.与strcpy相比,memcpy并不是遇到’\0’就结束,而是一定会拷贝完n个字节。
3.memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;
4. 要注意,它是以字节为单位一个一个拷贝!!!(不幸跌入这个坑)
5. memcpy函数没有处理内存重叠问题,要注意。

3. 顺带记录一下邻居memmove函数

众所周知,这两个函数最大的不同就是一个需要程序员自己控制内存,不能重叠,否则会出错;另一个即memmove函数则解决了这个内存区域重叠的问题。

memmove函数原理

void* memmove(void* dest, const void* src, size_t n)
{
    char * d  = (char*) dest;
    const char * s = (const char*) src;
    assert(dest&&src);
    if (s>d)
    {
         // 无内存重叠,从低地址拷贝
         while (n--)
            *d++ = *s++;
    }
    else if (s<d)
    {
        // 有内存重叠,从高地址拷贝,将指针调到最后
        d = d+n-1;
        s = s+n-1;
  
        while (n--)
           *d-- = *s--;
    }
	
    return dest;
}

在此不做过多介绍,文末附上大佬链接,感兴趣的可以去研究。

4. 坑

我定义并初始化的这个指针数组,假设长度为nLength。我在zmq的发送端Req通过指针传参是这样复制的:

void CRequest::SendRequest(int* rawData, int nLength)
{
	 void *context = zmq_init(1);
	 void *z_socket = zmq_socket(context, ZMQ_REQ);

	 // 绑定ip端口
	 string strConInfo = m_strConInfo;
	 zmq_connect(z_socket, strConInfo.c_str());  //建立ip+端口连接
	 //发送部分
	 zmq_msg_t send_msg;
	 //方式1——错误
	 //int nRc = zmq_msg_init_size(&send_msg, nLength);//初始化指令信息
	 //memcpy((int *)zmq_msg_data(&send_msg), rawData, nLength);//拷贝
	 //方式1——改正
	 //int nRc = zmq_msg_init_size(&send_msg, nLength*4);//初始化指令信息
	 //memcpy((int *)zmq_msg_data(&send_msg), rawData, nLength*4);
	 //方式2
	 int nRc = zmq_msg_init_data(&send_msg, rawData, nLength, NULL, NULL);//错误
	//int nRc = zmq_msg_init_data(&send_msg, rawData, nLength*4, NULL, NULL);//改正
	 zmq_msg_send(&send_msg, z_socket, 0);  //发送请求
	 zmq_msg_close(&send_msg);
	 //接收部分,略
	 ...
	 zmq_close(z_socket);
	 zmq_term(context);
}

两种方式本质都是一样的,这里由于是基于字节拷贝的,一直没注意到,导致把长度作为size。发到另一端后,遍历指针数组时,后面部分数据输出直接一堆乱码。代码庞大,检查了好久快要躺尸时,最终定位到了这里,害。

5. 解决

解决办法就很明了了,将nLength*4,(int型是4字节),当然在接收端遍历时也要再除以4恢复指针数组的长度。

————
有时候,一个问题没解决前觉得怎么这么难,可真当解决了吧,又觉得这么简单怎么就没想到,可真让人头大。

链接:
memcpy和memmove函数的区别和实现

memcpy()函数是C语言中的一个库函数,用于将一段内存的内容复制到另一段内存中。它的函数原型如下: ```c void *memcpy(void *dest, const void *src, size_t n); ``` 其中,`dest`是目标内存的起始地址,`src`是源内存的起始地址,`n`是要复制的字节数。 memcpy()函数的作用是将源内存中的内容复制到目标内存中,它可以用于复制任意类型的数据,包括基本数据类型、结构体、数组等。它是一种高效的内存拷贝方法,通常比逐字节复制更快。 然而,memcpy()函数存在一些潜在的危害,主要包括以下几点: 1. 内存越界:如果源内存或目标内存的起始地址不合法,或者复制的字节数超过了目标内存的大小,就会导致内存越界错误。这可能会导致程序崩溃、数据损坏或安全漏洞。 2. 内存重叠:如果源内存和目标内存存在重叠部分,并且复制的字节数较大,就可能导致未定义行为。这是因为memcpy()函数是按字节复制的,如果重叠部分被重复复制,就会导致数据错误。 为了避免这些危害,使用memcpy()函数时需要注意以下几点: 1. 确保源内存和目标内存的起始地址合法,并且目标内存有足够的空间来容纳复制的数据。 2. 确保源内存和目标内存没有重叠部分,或者使用memmove()函数来处理重叠情况。 3. 注意字节数的正确性,避免越界访问。 4. 在使用memcpy()函数时,尽量使用类型安全的方式,避免类型不匹配导致的错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值