重写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!
问题分析与修复
初始版本核心问题
- 空指针检查顺序错误:先调用
my_strlen(src)再检查src是否为NULL,会触发NULL指针解引用;且dest的空指针检查也在my_strlen(dest)之后,存在同样风险。 sizeof(dest)误用:函数参数中的数组会退化为指针,sizeof(dest)得到的是指针大小(如64位系统为8字节),而非目标数组的实际容量,导致空间检查逻辑完全失效。- 重复计算
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
相关产品推荐
相关产品推荐

