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

重写C标准库strcat函数时的字符串重叠验证问题排查

重写strcat时的错误处理与拼接失效问题

问题背景

我正在重写C标准库的strcat()函数,添加了字符串重叠检查逻辑,但程序始终进入错误流程返回NULL。调整参数后,返回地址正常,但字符串拼接仍未生效。


初始实现代码

my_strcat 函数

char *my_strcat(char *restrict dest, const char *restrict src)
{
    size_t dest_len = 0, src_len = 0;
    char *p = dest;
    src_len = my_strlen(src);
    if (!dest || !src)
        return NULL;
    dest_len = my_strlen(dest);
    if (src >= dest && src < dest + dest_len) {
        return NULL;
    }
    if (dest >= src && dest < src + src_len) {
        return NULL;
    }
    while (*p != '\0') p++, dest_len++;
    if (dest_len + src_len + 1 > sizeof(dest))
        return NULL;
    p = dest + dest_len;
    while (*src != '\0')
        *p++ = *src++;
    *p = '\0';
    return dest;
}

my_strlen 函数

size_t my_strlen(const char *s)
{
    size_t count = 0;
    if (s != NULL) {
        while (*s != 0) {
            count++;
            s++;
        }
    }
    return count;
}

测试代码

int main(int argc, char **argv)
{
    const char *src = "Hello";
    char dest[100] = " world!";
    char *test = my_strcat(dest, src);
    printf("Src : %s Dest : %s\n", src, dest);
    printf("Return adress : %p, Value : %s\n", test, test);
    return 0;
}

GDB 调试信息

if (src >= dest && src < dest + dest_len) 
1: dest = 0x7fffffffda70 " world!"
2: src = 0x555555557004 "Hello"
3: dest_len = 0
4: src_len = 5

初始程序输出

Src : Hello Dest :  world!
Return adress : (nil), Value : (null)

更新后的代码与问题

根据建议新增d_size参数修改函数:

char *my_strcat(char *restrict dest, const char *restrict src, size_t d_size)
{
    size_t dest_len = 0, src_len = 0;
    char *p = dest;
    if (!dest || !src)
        return NULL;
    src_len = my_strlen(src);
    dest_len = my_strlen(dest);
    if (src >= dest && src < dest + dest_len) {
        return NULL;
    }
    if (dest >= src && dest < src + src_len) {
        return NULL;
    }
    while (*p != '\0') p++, dest_len++;
    if (dest_len + src_len + 1 > d_size)
        return NULL;
    p = dest + dest_len;
    while (*src != '\0')
        *p++ = *src++;
    *p = '\0';
    return dest;
}

调用方式修改为:

char *test = my_strcat(dest, src, sizeof(dest));

但拼接仍未生效,输出:

Src : Hello Dest :  world!
Return adress : 0x7fff74bc5650, Value :  world!

问题分析与修复

初始版本核心问题

  1. 空指针检查顺序错误:先调用my_strlen(src)再检查src是否为NULL,会触发NULL指针解引用;且dest的空指针检查也在my_strlen(dest)之后,存在同样风险。
  2. sizeof(dest)误用:函数参数中的数组会退化为指针,sizeof(dest)得到的是指针大小(如64位系统为8字节),而非目标数组的实际容量,导致空间检查逻辑完全失效。
  3. 重复计算dest_len:先通过my_strlen(dest)得到正确长度,又通过while循环再次累加,导致dest_len翻倍,后续指针偏移错误。

更新版本遗留问题

修复了sizeof的问题,但重复计算dest_len的错误未修正:翻倍后的长度让p = dest + dest_len指向了目标字符串末尾之外的位置,拼接内容被写入错误地址,原字符串未被修改。

最终修复代码

char *my_strcat(char *restrict dest, const char *restrict src, size_t d_size)
{
    size_t dest_len, src_len;
    char *p;

    // 优先做空指针检查
    if (!dest || !src)
        return NULL;

    src_len = my_strlen(src);
    dest_len = my_strlen(dest);

    // 字符串重叠检查
    if ((src >= dest && src < dest + dest_len) || 
        (dest >= src && dest < src + src_len)) {
        return NULL;
    }

    // 检查目标空间是否足够(需包含终止符)
    if (dest_len + src_len + 1 > d_size)
        return NULL;

    // 定位到目标字符串末尾
    p = dest + dest_len;

    // 拼接源字符串
    while (*src != '\0')
        *p++ = *src++;
    *p = '\0';

    return dest;
}

总结

  • 空指针检查必须放在所有指针解引用操作之前;
  • 函数参数中的数组无法通过sizeof获取容量,需显式传入;
  • 避免重复计算字符串长度,防止指针偏移错误;
  • 重叠检查逻辑本身正确,但依赖正确的长度计算才能生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 16:25:35