You need to enable JavaScript to run this app. C语言自定义随机字符串生成函数第二次调用var_ran_string输出乱码
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.29 02:45:03