自制memcpy()实现末尾出现冗余字节的问题排查与修复求助
自定义memcpy实现的末尾冗余字节问题修复
问题背景
我用C实现了一个优化版memcpy,通过按2的幂次块(先8字节uint64_t,再4字节uint32_t、2字节uint16_t,最后1字节)复制来提升速度。多数场景运行正常,但当原字符串长度为72或24这类8字节整数倍时,复制后的字符串末尾会出现冗余字节,strlen返回的长度比预期大。
原实现代码
#include <stdio.h> #include <stddef.h> #include <stdint.h> #include <stdlib.h> static inline size_t detalign(size_t size) { if (size == 0) return 0; if (!(size & 0x7)) return 8; if (!(size & 0x3)) return 4; if (!(size & 0x1)) return 2; else return 1; } size_t strlen(const char *str) { size_t k = 0; while (str[k] != '\0') k++; return k; } void *memcpy(void *restrict dest, const void *restrict src, size_t count) { if (count < 2048) { int sizes[4] = { 8, 4, 2, 1 }; int pos = 0, rem = count; while (rem > 0) { // 找到适配剩余长度的最大2次幂块大小 int divisor = 0; for (int i = 0; i < 4; i++) { if (rem / sizes[i] && sizes[i] > divisor) divisor = sizes[i]; } int iter = 0; int reps = rem / divisor; switch (divisor) { case 8: uint64_t *restrict qdest = (uint64_t *) (dest + pos), *restrict qsrc = (uint64_t *) (src + pos); for (int j = 0; j < reps; j++) qdest[j] = qsrc[j]; printf("Iteration %d: start position %d, end position %d, size %d.\n", iter, pos, pos + (reps * divisor), divisor); break; case 4: uint32_t *restrict ddest = (uint32_t *) (dest + pos), *restrict dsrc = (uint32_t *) (src + pos); for (int j = 0; j < reps; j++) ddest[j] = dsrc[j]; printf("Iteration %d: start position %d, end position %d, size %d.\n", iter, pos, pos + (reps * divisor), divisor); break; case 2: uint16_t *restrict hdest = (uint16_t *) (dest + pos), *restrict hsrc = (uint16_t *) (src + pos); for (int j = 0; j < reps; j++) hdest[j] = hsrc[j]; printf("Iteration %d: start position %d, end position %d, size %d.\n", iter, pos, pos + (reps * divisor), divisor); break; case 1: uint8_t *restrict bdest = (uint8_t *) (dest + pos), *restrict bsrc = (uint8_t *) (src + pos); for (int j = 0; j < reps; j++) bdest[j] = bsrc[j]; printf("Iteration %d: start position %d, end position %d, size %d.\n", iter, pos, pos + (reps * divisor), divisor); break; } pos += reps * divisor; rem -= reps * divisor; iter++; } } } int main() { char *string1 = "This is an example string. Please note I have not precomputed the length."; size_t length = strlen((const char *) string1); char *string2 = (char *) malloc(length * sizeof(char)); memcpy(string2, string1, length); printf("\n"); printf("STRING 1 (%d bytes): \"%s\"\n", length * sizeof(char), string1); printf("STRING 2 (%d bytes): \"%s\"\n", strlen(string2) * sizeof(char), string2); }
问题原因
- 字符串终止符缺失:
strlen返回的是原字符串中'\0'之前的字节数,因此memcpy仅复制了length个有效字符,并未包含终止符。原代码中malloc只分配了length字节,没有多余空间存储'\0',导致strlen(string2)会越界读取内存中的随机数据,直到遇到某个'\0',从而出现冗余字节。 - memcpy未遵循标准:标准
memcpy要求返回目标指针dest,原实现未处理该逻辑,虽不直接导致当前问题,但不符合规范。 - 大长度分支未实现:当
count >= 2048时,memcpy未执行任何复制操作,属于功能缺失。 - 迭代计数错误:原代码中
iter变量始终初始化为0,导致输出的迭代次数全部为0,无实际排查意义。
修复后的代码
#include <stdio.h> #include <stddef.h> #include <stdint.h> #include <stdlib.h> static inline size_t detalign(size_t size) { if (size == 0) return 0; if (!(size & 0x7)) return 8; if (!(size & 0x3)) return 4; if (!(size & 0x1)) return 2; else return 1; } size_t strlen(const char *str) { size_t k = 0; while (str[k] != '\0') k++; return k; } void *memcpy(void *restrict dest, const void *restrict src, size_t count) { // 处理空输入或count为0的情况 if (dest == NULL || src == NULL || count == 0) return dest; int sizes[4] = { 8, 4, 2, 1 }; int pos = 0, rem = count; int iter = 0; while (rem > 0) { // 找到适配剩余长度的最大2次幂块大小 int divisor = 0; for (int i = 0; i < 4; i++) { if (rem / sizes[i] && sizes[i] > divisor) divisor = sizes[i]; } int reps = rem / divisor; switch (divisor) { case 8: uint64_t *restrict qdest = (uint64_t *) (dest + pos), *restrict qsrc = (uint64_t *) (src + pos); for (int j = 0; j < reps; j++) qdest[j] = qsrc[j]; printf("Iteration %d: start position %d, end position %d, size %d.\n", iter, pos, pos + (reps * divisor), divisor); break; case 4: uint32_t *restrict ddest = (uint32_t *) (dest + pos), *restrict dsrc = (uint32_t *) (src + pos); for (int j = 0; j < reps; j++) ddest[j] = dsrc[j]; printf("Iteration %d: start position %d, end position %d, size %d.\n", iter, pos, pos + (reps * divisor), divisor); break; case 2: uint16_t *restrict hdest = (uint16_t *) (dest + pos), *restrict hsrc = (uint16_t *) (src + pos); for (int j = 0; j < reps; j++) hdest[j] = hsrc[j]; printf("Iteration %d: start position %d, end position %d, size %d.\n", iter, pos, pos + (reps * divisor), divisor); break; case 1: uint8_t *restrict bdest = (uint8_t *) (dest + pos), *restrict bsrc = (uint8_t *) (src + pos); for (int j = 0; j < reps; j++) bdest[j] = bsrc[j]; printf("Iteration %d: start position %d, end position %d, size %d.\n", iter, pos, pos + (reps * divisor), divisor); break; } pos += reps * divisor; rem -= reps * divisor; iter++; } // 遵循标准返回目标指针 return dest; } int main() { char *string1 = "This is an example string. Please note I have not precomputed the length"; size_t length = strlen((const char *) string1); // 预留终止符空间 char *string2 = (char *) malloc((length + 1) * sizeof(char)); if (string2 == NULL) { perror("malloc failed"); return 1; } memcpy(string2, string1, length); // 手动添加字符串终止符 string2[length] = '\0'; printf("\n"); printf("STRING 1 (%zu bytes): \"%s\"\n", length, string1); printf("STRING 2 (%zu bytes): \"%s\"\n", strlen(string2), string2); // 释放内存避免泄漏 free(string2); return 0; }
修复说明
- 补充字符串终止符:
malloc时分配length + 1字节,复制完成后手动添加string2[length] = '\0';,确保字符串正确终止,避免strlen越界读取。 - 完善memcpy标准实现:添加空指针和
count=0的边界处理,函数末尾返回dest指针,符合C标准库规范。 - 修复迭代计数:将
iter变量移到循环外初始化,确保迭代次数输出正确,便于排查问题。 - 添加内存泄漏防护:在
main函数中添加malloc失败判断,并在最后释放内存。
内容的提问来源于stack exchange,提问作者ecfedele
相关产品推荐
相关产品推荐

