You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自制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);
}

问题原因

  1. 字符串终止符缺失:strlen返回的是原字符串中'\0'之前的字节数,因此memcpy仅复制了length个有效字符,并未包含终止符。原代码中malloc只分配了length字节,没有多余空间存储'\0',导致strlen(string2)会越界读取内存中的随机数据,直到遇到某个'\0',从而出现冗余字节。
  2. memcpy未遵循标准:标准memcpy要求返回目标指针dest,原实现未处理该逻辑,虽不直接导致当前问题,但不符合规范。
  3. 大长度分支未实现:当count >= 2048时,memcpy未执行任何复制操作,属于功能缺失。
  4. 迭代计数错误:原代码中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;
}

修复说明

  1. 补充字符串终止符:malloc时分配length + 1字节,复制完成后手动添加string2[length] = '\0';,确保字符串正确终止,避免strlen越界读取。
  2. 完善memcpy标准实现:添加空指针和count=0的边界处理,函数末尾返回dest指针,符合C标准库规范。
  3. 修复迭代计数:将iter变量移到循环外初始化,确保迭代次数输出正确,便于排查问题。
  4. 添加内存泄漏防护:在main函数中添加malloc失败判断,并在最后释放内存。

内容的提问来源于stack exchange,提问作者ecfedele

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 22:48:10