C语言自定义随机字符串生成函数第二次调用var_ran_string输出乱码
乱码问题的根因排查与修复方案
核心共性错误:未遵循C语言字符串的终止符规则
所有C语言标准库中处理字符串的函数(strcat、printf、strlen等),都要求字符串必须以ASCII码为0的'\0'作为结尾标记,代码全程没有主动添加这个标记,是乱码的核心诱因。
逐函数拆解具体问题
ran_string 函数的问题
- 生成的字符串没有合法终止符:循环填充完
tam个随机字符后,没有在数组最后一位设置'\0'。返回的指针指向的内存是一整段无结尾标记的随机字符,不管是作为独立字符串输出,还是作为strcat的读取源,函数都会一直向后遍历内存直到碰到碰巧存在的'\0',中途就会读出堆上的残留脏数据。 - 内存分配量不足:计算得到字符串长度为
tam时,没有为终止符预留1个字节的内存空间,哪怕后续手动加终止符,也会直接越界写相邻内存。 - 长度计算边界无防护:当前
tam = rand()%max得到的范围是0 ~ max-1,校验完最小值min后实际有效字符串长度范围是min ~ max-1,本身逻辑没问题,但没有处理max <=0的非法入参,会触发除零错误。
var_ran_string 函数的问题
这是导致“只有第二次调用出乱码”的直接原因:
- 动态分配的缓冲区未初始化:
a = malloc(...)得到的堆内存是之前程序释放后的残留数据,没有在使用前将缓冲区的第一个位置设为'\0'。第一次调用时,新申请的堆块刚好残留的开头值为0,strcat能正确找到写入起始点,碰巧能正常运行;第二次调用时,申请的堆块复用了之前被写入过的内存,开头位置全是非0的脏数据,strcat会从缓冲区位置向后一直搜索直到找到第一个碰巧存在的'\0',搜索位置已经远远超出分配的内存范围,直接发生越界读写。 - 内存容量计算不足:当前分配的空间是
num * max,没有计算num个字符串之间需要插入的num-1个空格,也没有预留整个拼接后字符串的终止符空间,写入过程中很容易越界踩坏相邻内存。 - 内存泄漏:每次调用
ran_string返回的堆内存,没有在strcat拷贝完成后释放,多次调用后堆内存会被持续占用,最终触发内存损坏。
修复后的参考代码
#include <stdlib.h> #include <string.h> #include <stdio.h> char *ran_string(int max, int min){ // 入参合法性校验 if (max <= 0 || min <=0 || min > max) { return NULL; } int tam = rand() % max; // 保证长度在[min, max-1]区间 if (tam < min) tam = min; // 多分配1字节给字符串终止符 char *str = malloc(sizeof(char) * (tam + 1)); for (int i=0; i<tam; i++) { if(i==0){ str[i] = (char)((rand() % 26) + 65); // 大写字母 } else { str[i] = (char)((rand() % 26) + 97); // 小写字母 } } // 手动添加字符串终止符 str[tam] = '\0'; return str; } char *var_ran_string(int num, int max, int min){ // 入参合法性校验 if (num <=0 || max <=0 || min <=0 || min > max) { return NULL; } // 计算总需要的容量:每个字符串最长max-1,加num-1个空格,加1个终止符 int total_size = num * max + num; char *a = malloc(sizeof(char) * total_size); // 初始化缓冲区为合法空字符串,给strcat提供正确的写入起始点 a[0] = '\0'; // 生成第一个随机字符串 char *temp_str = ran_string(max, min); strcat(a, temp_str); free(temp_str); // 释放临时字符串,避免内存泄漏 for(int i=0; i<num-1; i++){ strcat(a, " "); temp_str = ran_string(max, min); strcat(a, temp_str); free(temp_str); } return a; }
为什么只有第二次调用出问题
堆内存的分配规则是优先复用之前释放的内存块,第一次调用var_ran_string时,系统刚分配的堆块往往是从操作系统申请的全新内存,内部残留值大多为0,strcat搜索起始位置时刚好能在缓冲区开头找到'\0',不会触发越界;第二次调用时,申请的内存块会复用第一次调用时释放的内存,内部全是之前写入的非0残留数据,strcat找不到合法起始标记,直接在越界位置读写,就会输出夹杂特殊控制字符的乱码。
内容的提问来源于stack exchange,提问作者Leonh kapa
相关产品推荐
相关产品推荐

