void* mymemcpy( void* dest, const void* src, size_t count )

本文深入探讨了memcpy函数的高效实现方法,包括防止地址重叠、指针类型转换及优化分支预测,通过特殊技巧减少跳转和提升指令执行效率。

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

void* mymemcpy( void* dest, const void* src, size_t count )
{
        assert(dest!=NULL);
        assert(src!=NULL);
        char* pdest=(char*)dest;
        char* psrc=(char*)src;
        if(psrc<pdest&&psrc+count>pdest)
        {
                psrc=psrc+count-1;
                pdest=pdest+count-1;
                while(count-->0)
                        *pdest--=*psrc--;
         }
         else
         {
                while(count-->0)
                       *pdest++=*psrc++;
          }
          return dest;
}

注意防止地址有重叠部分,还有指针类型转换,这是常规实现方法;

void* mymemcpy( void* dest, const void* src, size_t count )  

{                                                                                  

    char* d = (char*)dest;                                                 

    const char* s = (const char*)src ;                                 

    int n = (count + 7) / 8; // count > 0 assumed                   

    switch( count & 7 )                                                      

    {                                                                              

    case 0:  do {  *d++ = *s++;                                         

    case 7:        *d++ = *s++;                                           

    case 6:        *d++ = *s++;                                           

    case 5:        *d++ = *s++;                                           

    case 4:        *d++ = *s++;                                           

    case 3:        *d++ = *s++;                                           

    case 2:        *d++ = *s++;                                           

    case 1:        *d++ = *s++;                                           

               } while (--n > 0);                                             

    }                                                                              

    return dest;                                                                

}       

这程序写的太变态了,乍一看,不知所云,细看一下,琢磨一下还可以:

摘自原贴:

int n = (count + 7) / 8;计算要复制的轮数(每轮复制8位),剩下的余数位数也要复制进去。
count & 7控制要复制余数位数,while (--n > 0)控制轮数。
比如count = 9,则n = 2,count & 7 = 1,要复制2轮,程序跳到case1执行,复制一位之后,再循环一轮,复制8位.

为何这么实现:引用上面帖子后面的评论:

每8个字节拷贝省7个跳转(AMD处理器内部分支预测算法是“假定全都跳转”,因此这里也就是相当于省了7个时钟周期);把swith和do循环凑到一起,多少省几个字节的空间…… 
但使用32位或64位指令,可以得到更大的优化效果,代码还要精炼得多。 
说实在话:除非内存小到极点、并且无法增加的系统,写出这样的程序纯属作孽……



#include "malloc.h" #include <string.h> // 内部内存池(32字节对齐,便于DMA操作) __align(32) u8 mem1base[MEM1_MAX_SIZE]; // 内存管理表(记录每个内存块的状态) u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; void my_mem_init(u8 memx); u8 my_mem_perused(u8 memx); // 内存管理参数常量数组 const u32 memtblsize[SRAMBANK] = {MEM1_ALLOC_TABLE_SIZE}; // 管理表大小 const u32 memblksize[SRAMBANK] = {MEM1_BLOCK_SIZE}; // 内存块大小 const u32 memsize[SRAMBANK] = {MEM1_MAX_SIZE}; // 内存池总大小 // 全局内存管理设备实例 struct _m_mallco_dev mallco_dev = { my_mem_init, // 指向初始化函数 my_mem_perused, // 指向内存使用率函数 {mem1base}, // 内存池基地址(仅内部内存) {mem1mapbase}, // 内存管理表基地址 {0} // 初始化状态(0=未初始化) }; // 内存复制函数(内部使用) void mymemcpy(void *des, void *src, u32 n) { u8 *xdes = des; u8 *xsrc = src; while(n--)*xdes++=*xsrc++; // 逐字节复制 } // 内存设置函数(内部使用) void mymemset(void *s, u8 c, u32 count) { u8 *xs = s; while(count--)*xs++=c; // 逐字节设置 } // 内存池初始化(内部调用) void my_mem_init(u8 memx) { // 清零内存管理表(初始化所有块为空闲) mymemset(mallco_dev.memmap[memx], 0, memtblsize[memx]*2); // 标记内存池已初始化 mallco_dev.memrdy[memx]=1; } // 获取内存池使用率(内部调用) u8 my_mem_perused(u8 memx) { u32 used=0; // 遍历管理表,统计已用块数 for(u32 i=0;i<memtblsize[memx];i++) { if(mallco_dev.memmap[memx][i])used++; } // 计算并返回使用百分比 return (used*100)/(memtblsize[memx]); } // 内存分配核心算法(内部调用) u32 my_mem_malloc(u8 memx,u32 size) { signed long offset=0; u32 nmemb; // 需要的内存块数 u32 cmemb=0; // 当前找到的连续空闲块数 // 检查内存池是否初始化 if(!mallco_dev.memrdy[memx])mallco_dev.init(memx); // 计算需要的内存块数(向上取整) nmemb=size/memblksize[memx]; if(size%memblksize[memx])nmemb++; // 从内存池尾部向前搜索连续空闲块 for(offset=memtblsize[memx]-1;offset>=0;offset--) { if(!mallco_dev.memmap[memx][offset])cmemb++; // 空闲块计数 else cmemb=0; // 中断连续 // 找到足够的连续空闲块 if(cmemb==nmemb) { // 标记这些块为已分配(存储分配块数) for(u32 i=0;i<nmemb;i++) { mallco_dev.memmap[memx][offset+i]=nmemb; } return (offset*memblksize[memx]); // 返回偏移地址 } } return 0XFFFFFFFF; // 分配失败(内存不足) } // 内存释放(内部调用) u8 my_mem_free(u8 memx,u32 offset) { // 检查内存池是否初始化 if(!mallco_dev.memrdy[memx]) { mallco_dev.init(memx); return 1; // 返回未初始化错误 } // 检查偏移是否有效 if(offset<memsize[memx]) { // 计算块索引 int index=offset/memblksize[memx]; // 获取该块分配时记录的块数 int nmemb=mallco_dev.memmap[memx][index]; // 释放连续的内存块(标记为空闲) for(int i=0;i<nmemb;i++) { mallco_dev.memmap[memx][index+i]=0; } return 0; // 成功 } return 2; // 偏移超出范围 } // 释放内存(用户接口) void myfree(u8 memx,void *ptr) { if(ptr==NULL)return; // 空指针检查 // 计算偏移量(地址 - 基地址) u32 offset=(u32)ptr-(u32)mallco_dev.membase[memx]; my_mem_free(memx,offset); // 调用内部释放 } // 分配内存(用户接口) void *mymalloc(u8 memx,u32 size) { // 调用内部分配函数获取偏移 u32 offset=my_mem_malloc(memx,size); if(offset==0XFFFFFFFF)return NULL; // 分配失败 else return (void*)((u32)mallco_dev.membase[memx]+offset); // 返回绝对地址 }#ifndef __MALLOC_H #define __MALLOC_H #include "stm32f10x.h" // 内存池定义:STM32F103C8T6只有内部RAM(20KB),不包含外部RAM #define SRAMIN 0 // 内部内存池编号 #define SRAMBANK 1 // 内存池数量(仅使用1个) // 内存管理参数调整(适配C8T6的20KB RAM) #define MEM1_BLOCK_SIZE 32 // 内存块大小设为32字节(提高小内存分配效率) #define MEM1_MAX_SIZE 12 * 1024 // 分配给内存管理的最大内存(12KB,保留8KB给堆栈和系统) #define MEM1_ALLOC_TABLE_SIZE (MEM1_MAX_SIZE/MEM1_BLOCK_SIZE) // 管理表大小 // 内存管理设备结构体 struct _m_mallco_dev { void (*init)(u8); // 初始化函数指针 u8 (*perused)(u8); // 内存使用率计算函数指针 u8 *membase[SRAMBANK]; // 内存池基地址数组 u16 *memmap[SRAMBANK]; // 内存池管理表基地址数组 u8 memrdy[SRAMBANK]; // 内存池初始化状态标志 }; extern struct _m_mallco_dev mallco_dev; // 声明全局内存管理设备 // 用户接口函数声明 void myfree(u8 memx, void *ptr); // 释放内存(外部调用) void *mymalloc(u8 memx, u32 size); // 分配内存(外部调用) u8 my_mem_perused(u8 memx); // 获取内存使用率(外部调用) #endif #include "RingBuffer.h" #include <stdlib.h> #include <string.h> // RingBuffer.h #ifndef min #define min(a, b) (((a) < (b)) ? (a) : (b)) #endif // 判断一个数是否是2的幂(环形缓冲区要求) static bool is_power_of_2(uint32_t x) { return (x != 0) && ((x & (x - 1)) == 0); } // 向上取整到2的幂(确保缓冲区大小符合要求) static uint32_t roundup_pow_of_two(uint32_t x) { uint32_t b = 1; // 从1开始 // 找到不小于x的最小2的幂 while (b < x) { b <<= 1; // 左移一位(相当于×2) } return b; } // 创建并初始化环形缓冲区 RingBuffer *RingBuffer_Malloc(uint32_t size) { // 分配管理结构体内存 RingBuffer *fifo = RING_BUFFER_MALLOC(sizeof(RingBuffer)); if(!fifo) return NULL; // 分配失败 // 调整大小为2的幂 if(!is_power_of_2(size)) size = roundup_pow_of_two(size); // 分配缓冲区内存 fifo->buffer = RING_BUFFER_MALLOC(size); if(!fifo->buffer) { RING_BUFFER_FREE(fifo); return NULL; // 分配失败 } // 初始化结构体成员 fifo->size = size; fifo->in = fifo->out = 0; fifo->use_dma = false; // 默认不启用DMA return fifo; } // 释放环形缓冲区 void RingBuffer_Free(RingBuffer *fifo) { if(fifo) { RING_BUFFER_FREE(fifo->buffer); // 先释放缓冲区 RING_BUFFER_FREE(fifo); // 再释放结构体 } } // 向环形缓冲区写入数据 uint32_t RingBuffer_In(RingBuffer *fifo, void *in, uint32_t len) { // 参数检查 if(!fifo || !in) return 0; // 计算实际可写入长度(取请求长度和可用空间的最小值) len = min(len, RingBuffer_Avail(fifo)); if(len == 0) return 0; // 计算第一部分长度(从写索引到缓冲区末尾) uint32_t l = min(len, fifo->size - (fifo->in & (fifo->size - 1))); // 复制第一部分数据 memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), in, l); // 复制剩余部分数据(从缓冲区开头继续) if(len > l) { memcpy(fifo->buffer, (uint8_t *)in + l, len - l); } // 原子更新写索引(缓冲区大小是2的幂,自然溢出安全) fifo->in += len; return len; // 返回实际写入字节数 } // 从环形缓冲区读取数据 uint32_t RingBuffer_Out(RingBuffer *fifo, void *out, uint32_t len) { // 参数检查 if(!fifo || !out) return 0; // 计算实际可读取长度(取请求长度和已用空间的最小值) len = min(len, RingBuffer_Len(fifo)); if(len == 0) return 0; // 计算第一部分长度(从读索引到缓冲区末尾) uint32_t l = min(len, fifo->size - (fifo->out & (fifo->size - 1))); // 复制第一部分数据 memcpy(out, fifo->buffer + (fifo->out & (fifo->size - 1)), l); // 复制剩余部分数据(从缓冲区开头继续) if(len > l) { memcpy((uint8_t *)out + l, fifo->buffer, len - l); } // 原子更新读索引 fifo->out += len; return len; // 返回实际读取字节数 } // DMA初始化(预留功能) void RingBuffer_InitDMA(RingBuffer *fifo, DMA_Channel_TypeDef *dma_tx, DMA_Channel_TypeDef *dma_rx) { if(!fifo) return; fifo->use_dma = true; // 启用DMA标志 // 此处实现DMA配置代码 // 实际项目中需配置DMA通道、地址、长度等参数 // 启用USART的DMA请求等 } #ifndef __RINGBUFFER_H #define __RINGBUFFER_H #include <stdint.h> #include <stdbool.h> #include "malloc.h" // 使用自定义内存管理 // 定义内存分配宏(使用内部内存池) #define RING_BUFFER_MALLOC(size) mymalloc(SRAMIN, size) #define RING_BUFFER_FREE(block) myfree(SRAMIN, block) // 环形缓冲区结构体 typedef struct { uint8_t *buffer; // 缓冲区基地址 uint32_t size; // 缓冲区大小(必须是2的幂) volatile uint32_t in; // 写索引(生产者位置)volatile防止编译器优化 volatile uint32_t out; // 读索引(消费者位置)volatile防止编译器优化 bool use_dma; // DMA使用标志(预留扩展) } RingBuffer; // 函数声明 RingBuffer *RingBuffer_Malloc(uint32_t size); // 创建环形缓冲区 void RingBuffer_Free(RingBuffer *fifo); // 释放环形缓冲区 uint32_t RingBuffer_In(RingBuffer *fifo, void *in, uint32_t len); // 写入数据 uint32_t RingBuffer_Out(RingBuffer *fifo, void *out, uint32_t len); // 读取数据 void RingBuffer_InitDMA(RingBuffer *fifo, DMA_Channel_TypeDef *dma_tx, DMA_Channel_TypeDef *dma_rx); // DMA初始化(预留) // 内联辅助函数 static inline void RingBuffer_Reset(RingBuffer *fifo) { fifo->in = fifo->out = 0; // 重置缓冲区 } static inline uint32_t RingBuffer_Size(RingBuffer *fifo) { return fifo->size; // 返回缓冲区容量 } static inline uint32_t RingBuffer_Len(RingBuffer *fifo) { return fifo->in - fifo->out; // 已用空间(无锁,仅适用单生产者单消费者) } static inline uint32_t RingBuffer_Avail(RingBuffer *fifo) { return fifo->size - RingBuffer_Len(fifo); // 可用空间 } static inline bool RingBuffer_IsEmpty(RingBuffer *fifo) { return (RingBuffer_Len(fifo) == 0); // 缓冲区是否为空 } static inline bool RingBuffer_IsFull(RingBuffer *fifo) { return (RingBuffer_Avail(fifo) == 0); // 缓冲区是否已满 } #endif /* __RINGBUFFER_H */ 这些代码是否正确,有没有逻辑错误,能否实现环形缓冲区
最新发布
07-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值