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

为何realloc不返回连续内存块?C动态字符串分配代码问题排查

问题排查:realloc内存不连续与代码逻辑错误

一、realloc的内存分配机制

realloc的核心逻辑分两种情况:

  • 如果当前内存块后续有足够空闲空间,会直接扩展原内存块,返回原地址;
  • 如果当前内存块后续没有足够空间,会在堆上重新找一块足够大的空闲内存,把原内存的数据拷贝过去,然后释放原内存块,返回新的内存地址。

这就意味着realloc完全可能返回和原地址不连续的新内存,你的代码错误假设了realloc会一直扩展原内存块,导致后续指针计算全部失效。

二、代码中的核心错误

1. kts函数的问题

  • 初始malloc(1)后先赋值*string=0,紧接着又用getchar()覆盖该位置,逻辑冗余;
  • 循环中未检查realloc返回值:如果realloc失败会返回NULL,直接赋值会丢失原内存地址,导致内存泄漏;
  • 未处理EOF场景:当getchar()读到EOF时,返回值是-1,存入char类型会变成0xFF,永远不等于'\n',导致循环无法终止。

2. 主函数中字符串数组的逻辑错误

你试图让*string作为所有字符串的连续内存块,同时用string指针数组指向每个字符串起始位置,但逻辑完全错误:

  • kts函数已经为每个字符串单独分配了内存(堆上的分散块),和*string没有任何关联;
  • 后续对*string的realloc操作,只是单独分配了一块无关内存,用(*string)[i]访问自然会读到垃圾值;
  • string[k] = *string + ((sum + 1) * sizeof **string)的计算无意义:sizeof **string是sizeof(char)即1,且string指针数组被混合了两种完全无关的内存地址(kts分配的和*string分配的),导致指针彻底混乱。

3. 循环终止条件错误

主循环的ver && string && *string中,*string初始是未初始化的随机值,可能直接导致循环不执行;同时代码未处理EOF场景,所以不会循环到输入结束。

4. 内存释放错误

  • free(*string), string = NULL;之后,free(string), string = NULL;属于无意义操作(string已被设为NULL);
  • kts函数分配的所有字符串内存都未被释放,造成严重内存泄漏。

三、修正建议

1. 重新设计内存逻辑

如果你想实现连续内存块存储所有字符串,不要在kts中单独分配内存,而是在主函数维护一块连续内存,kts直接往这块内存写字符,同时记录每个字符串的起始偏移。

2. 修复后的kts函数

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

// 往指定缓冲区写入字符串,返回写入的字符数(含换行符)
unsigned int kts(char *buf, unsigned int buf_size) {
    unsigned int i = 0;
    int c;
    // 读取到换行、EOF或缓冲区满时停止
    while (i < buf_size - 1 && (c = getchar()) != EOF && c != '\n') {
        buf[i++] = (char)c;
    }
    buf[i] = '\0';
    // 若读到换行,计入长度
    if (c == '\n') {
        i++;
        buf[i-1] = '\n';
        buf[i] = '\0';
    }
    return i;
}

3. 主函数修正(连续内存版本)

int main() {
    unsigned int sum = 0, k = 0, alnum = 0, par = 0, i = 0;
    // 初始分配连续内存缓冲区
    char *buf = malloc(1024);
    if (!buf) return 1;
    unsigned int buf_capacity = 1024;
    // 记录每个字符串的起始偏移
    unsigned int *offsets = malloc(sizeof(unsigned int));
    if (!offsets) { free(buf); return 1; }
    unsigned int offsets_capacity = 1;
    int ver = 1;

    while (ver) {
        // 扩容缓冲区(确保有足够空间)
        if (sum + 100 > buf_capacity) {
            buf_capacity *= 2;
            char *new_buf = realloc(buf, buf_capacity);
            if (!new_buf) { free(buf); free(offsets); return 1; }
            buf = new_buf;
        }
        // 读取字符串到缓冲区
        unsigned int len = kts(buf + sum, buf_capacity - sum);
        if (len == 0 && feof(stdin)) {
            break; // 读到EOF,终止循环
        }
        // 扩容偏移数组
        if (k >= offsets_capacity) {
            offsets_capacity *= 2;
            unsigned int *new_offsets = realloc(offsets, offsets_capacity * sizeof(unsigned int));
            if (!new_offsets) { free(buf); free(offsets); return 1; }
            offsets = new_offsets;
        }
        offsets[k++] = sum;
        sum += len;

        // 检查是否以".\n"结尾
        if (len >= 2 && buf[sum - 2] == '.' && buf[sum - 1] == '\n') {
            ver = 0;
        }
    }

    // 遍历连续内存块
    puts("");
    for (i = 0; i < sum; i++) {
        putchar(buf[i]);
        if (isalnum((unsigned char)buf[i])) {
            alnum++;
            // 判断是否为单词开头
            if (i == 0 || !isalnum((unsigned char)buf[i - 1])) {
                par++;
            }
        }
    }

    printf("\nStrings: %u, Characters: %u, Alphanum: %u, Words: %u", k, sum, alnum, par);

    // 释放所有内存
    free(buf);
    free(offsets);
    return 0;
}

内容的提问来源于stack exchange,提问作者Luca Fiorentino Heredia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 11:27:14