为何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
相关产品推荐
相关产品推荐

