C语言拼接字符串与数字时sprintf源目同地址报错如何解决
问题根因
- C标准明确规定
sprintf的输入源缓冲区和输出目标缓冲区禁止重叠,你将同一个变量s同时作为格式化输入的%s参数和输出目标地址,属于未定义行为,因此出现运行异常。 - 你通过
malloc申请的内存未初始化,第一次读取s内容时得到的是内存垃圾值,也会导致输出异常。
解决方案
采用维护写入偏移量的方案规避缓冲区重叠问题:
- 新增一个变量记录当前结果字符串的写入位置偏移量
- 每次写入内容时,直接定位到结果缓冲区的偏移位置写入,无需重复读取整个已生成的字符串
- 推荐使用
snprintf替代sprintf,避免写入内容超出申请的缓冲区长度导致溢出。
修改后可运行的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> int main(int argc, char *argv[]) { // 校验输入参数合法性 if (argc < 2 || argv[1][0] == '\0') { printf("ERROR"); return 0; } char *input = argv[1]; int input_len = strlen(input); // 申请结果缓冲区,最坏情况每个字符都不重复,每个占2字节(字符+数字),加1存结束符 char *s = (char *)malloc(sizeof(char) * (input_len * 2 + 1)); if (s == NULL) { printf("ERROR"); return 0; } char prev = input[0]; if (isdigit((unsigned char)prev) != 0) { printf("ERROR"); free(s); return 0; } int count = 1; int offset = 0; // 记录当前写入偏移量 for (int i = 1; i < input_len; i++) { char curr = input[i]; if (isdigit((unsigned char)curr) != 0) { printf("ERROR"); free(s); return 0; } if (prev == curr) { count++; } else { // 往偏移位置写入,每次写入后更新偏移量 offset += snprintf(s + offset, (input_len * 2 + 1) - offset, "%c%d", prev, count); count = 1; prev = curr; } } // 写入最后一组统计结果 offset += snprintf(s + offset, (input_len * 2 + 1) - offset, "%c%d", prev, count); // 比较编码后长度和原串长度 if (strlen(s) > input_len) { printf("%s\n", input); } else { printf("%s\n", s); } free(s); return 0; }
关键修改说明
- 去掉了原代码中
sprintf格式化时读取s自身的逻辑,改为直接写入对应偏移位置,彻底解决缓冲区重叠问题 - 缓冲区申请长度根据输入长度动态计算,避免固定100字节的溢出风险
- 补充了内存申请失败、输入为空的边界校验,修复了
isdigit传入有符号字符可能导致的异常问题 - 提前计算输入字符串长度,避免循环中重复调用
strlen带来的性能损耗
内容的提问来源于stack exchange,提问作者user15409086
相关产品推荐
相关产品推荐

