C语言memcpy等函数如何处理不支持算术运算的void*数组
C标准库通用内存操作函数的实现逻辑
void*指针的处理核心逻辑
C标准确实明确禁止void*类型指针直接进行算术运算:因为void没有确定的大小,编译器无法计算偏移的步长。
标准库中所有接收void*参数的通用函数(比如memcpy、memmove、memset、qsort等),本质都是绕开了按原始类型操作的需求,直接把内存当成字节序列处理。
memcpy的简化实现原理
这类函数的核心操作只有两步:
- 首先把传入的
void*强制转换为unsigned char*类型,unsigned char的大小固定为1字节,对其做算术运算完全符合C标准要求 - 按照传入的字节长度参数,以单字节为单位完成内存的拷贝、移动、填充等操作
以下是无优化的最简memcpy实现样例:
void* my_memcpy(void* restrict dest, const void* restrict src, size_t byte_len) { unsigned char* d = (unsigned char*)dest; const unsigned char* s = (const unsigned char*)src; for (size_t i = 0; i < byte_len; i++) { d[i] = s[i]; } return dest; }
生产环境的memcpy会额外增加对齐判断、按机器字长批量拷贝、SIMD指令优化等逻辑,但核心的指针转换逻辑和上面的样例完全一致。
对你的通用list实现的参考方案
你目前遇到的void* data无法直接运算的问题,可以参考标准库的思路解决,常用有两种落地方式:
- 底层接口对标标准库设计:所有操作函数都要求传入单元素的字节长度,内部统一转
unsigned char*按字节偏移访问元素,示例接口如下:// 向list的pos位置插入元素 int list_insert(list_t* list, size_t pos, const void* elem, size_t elem_size); // 读取list的pos位置的元素指针,由调用方自行做类型转换 void* list_at(const list_t* list, size_t pos, size_t elem_size); - 上层用宏封装简化调用:保留你之前用宏实现模板的思路,把类型转换、元素大小计算的逻辑封装到宏里,用户调用时不需要手动处理类型和长度参数,示例宏如下:
// 自动计算元素大小、传入取址后的元素 #define LIST_INSERT(list, pos, elem, type) list_insert(list, pos, &(elem), sizeof(type)) // 自动把返回的void*转换为对应类型的左值 #define LIST_AT(list, pos, type) (*(type*)list_at(list, pos, sizeof(type)))
内容的提问来源于stack exchange,提问作者dumb_ptr
相关产品推荐
相关产品推荐

