【C语言进阶】使用memcpy你需要注意的一个问题
日常编程中,memcpy可以算得上是使用频次非常高的函数,那么有些小点可能你没有关注到,本文将给你提个小醒。
1 写在前面作为一个C语言程序员,标准库函数使用频次排行榜上,memcpy在上面的排行一定会非常靠前,就算排不了第一,肯定也排得上前三!
这个函数的使用虽然简单,但是在没有深入理解这个函数的时候,往往容易出问题。
本文将以一个实际的案例展开,带你全面了解这个函数。
2 问题现场问题现场是这样的,但是我正在调试stm32的一个DMA驱动代码,在DMA代码配置中使用的half-word模式,即半字,也就是双字节模式,所以我就定义个一个uint16_t的buffer,如下:
复制 #define DMA_SIZE 1024 uint16_t g_dma_buffer[DMA_SIZE]; 整体的实现思路就是,当需要DMA去搬运数据的时候,打卡DMA,等到DMA搬运完数据之后,触发一个信号量,然后应用层就过来拷贝数据。
于是我开了这样的一个接口以供应用层来拷贝数据。
复制 void copy_data_from_dma_buffer(uint8_t *data_out, uint16_t buffer_size) { //call memcpy … memcpy(data_out, (uint8_t *)g_dma_buffer, DMA_SIZE); } // 应用层调用的代码如下 uint8_t g_app_buffer[4096]; copy_data_from_dma_buffer(g_app_buffer, sizeof(g_app_buffer)); 那么问题来了,我那个memcpy使用正确了吗?
凭着多年使用memcpy的感觉,好像发现不对劲;
但是被调试代码的过程蒙晕了头脑,一时半会又想不起哪有问题!
3 知识点补充趁着这个机会,我好好地补充学习了关于memcpy的前前后后,重新梳理了一些它的知识点。
3.1 标准库对memcpy的描述
这个描述,我们可直接看Linux下面的man命令:
复制 NAME memcpy – copy memory area SYNOPSIS #include void *memcpy(void *dest, const void *src, size_t n); DESCRIPTION The memcpy() function copies n bytes from memory area src to memory area dest. The memory areas must not overlap. Use memmove(3) if the memory areas do overlap. RETURN VALUE The memcpy() function returns a pointer to dest. 请注意它的【描述】部分,这里说到它的第三个参数n,指的是【字节】数,而不是半字,也不是字;也就是它的所有拷贝计数都是按字节数来算的。
同时,还需要留意它的返回值,表示的是dest的原地址。
3.2 一个简易版本的源码实现
根据memcpy的描述,我们可以实现一个简易版本的memcpy,示例如下:
复制 void *my_memcpy(void *dest, const void *src, size_t n) { assert(dest && src && (n > 0)); if (dest == src) { ; } else { unsigned char *p_dest = (unsigned char *)dest; unsigned char *p_src = (unsigned char *)src; size_t i; for (i = 0; i < n; i++) { *p_dest++ = *p_src++; } } return dest; } 感兴趣的朋友,可以写写测试用例,测试测试该接口。
3.3 问题答疑
回到上面的问题点,毫无疑问,我之前的使用方法是错误的。
原因在于我在定义 DMA_SIZE 时,其实这个是双字节的计算,如果转换成单字节计数,还需要乘以个2。
也就是那段调用代码需要修改为:
复制 //call memcpy … memcpy(data_out, (uint8_t *)g_dma_buffer, 2 * DMA_SIZE); 那么有没有一个不考虑使用乘2的写法呢?自然是有的,我们可以借助sizeof来帮助我们求g_dma_buffer的字节数,修改如下:
复制 //call memcpy … memcpy(data_out, (uint8_t *)g_dma_buffer, sizeof(g_dma_buffer)); 至此,问题完美解决,驱动代码也没出问题了。
4 小小总结再次总结下几个小点吧。
memcpy的第三个参数n,指的是字节数,如果传入的src buffer不是单字节空间时,需要注意n的取值问题; memcpy函数的返回值表示的dest的原地址,并不是拷贝偏移之后的地址; memcpy本身不是安全操作,拷贝的过程中,需要调用者来保证数据拷贝不会溢出,这点也特别需要注意。 5 更多分享[架构师李肯]
架构师李肯 ( 全网同名 ),一个专注于嵌入式IoT领域的架构师。有着近10年的嵌入式一线开发经验,深耕IoT领域多年,熟知IoT领域的业务发展,深度掌握IoT领域的相关技术栈,包括但不限于主流RTOS内核的实现及其移植、硬件驱动移植开发、网络通讯协议开发、编译构建原理及其实现、底层汇编及编译原理、编译优化及代码重构、主流IoT云平台的对接、嵌入式IoT系统的架构设计等等。拥有多项IoT领域的发明专利,热衷于技术分享,有多年撰写技术博客的经验积累,连续多月获得RT-Thread官方技术社区原创技术博文优秀奖,荣获[CSDN博客专家]、[CSDN物联网领域优质创作者]、[2021年度CSDN&RT-Thread技术社区之星]、[2022年RT-Thread全球技术大会讲师]、[RT-Thread官方嵌入式开源社区认证专家]、[RT-Thread 2021年度论坛之星TOP4]、[华为云云享专家(嵌入式物联网架构设计师)]等荣誉。坚信【知识改变命运,技术改变世界】!
审核编辑:汤梓红
免责声明:文章内容来自互联网,本站不对其真实性负责,也不承担任何法律责任,如有侵权等情况,请与本站联系删除。
转载请注明出处:【C语言进阶】使用memcpy你需要注意的一个问题-c语言memcpy和memmove https://www.yhzz.com.cn/a/5955.html