C语言循环释放指针数组导致程序崩溃问题求助
问题描述
编写了riassemble函数返回char**指针数组,首元素是存储数组长度的char*。main函数循环释放数组元素时,释放第二个元素崩溃。已确认分配与释放地址一致,但无法定位问题。完整代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> void printarray(int* array, int arraysize){ for (int i = 0; i<arraysize; i++){ printf("%d\n", *array); array++; } } void printStrArray(char** array, int arraysize){ int j = 0; for (int i = 0; i<arraysize; i++){ j = 0; while (array[i][j] != '\0'){ printf("%c", array[i][j]); j++; } printf("\n"); } } int isStringInArray(char* string, char** stringArray, int arrayLen){ // returns 1 if string is contained in the array. for (int i = 0; i < arrayLen; i++){ if (strcmp(string, stringArray[i]) == 0){ //printf("%s is equal to %s %d\n", string, stringArray[i], i); return 1; } } return 0; } int lenstring(char* string){ // checks string length (works only if string has null character at the end.) char currchar = string[0]; int strlen = 0; while (currchar != '\0'){ strlen++; currchar = string[strlen]; } return strlen; } char** riassemble(char* stringa){ char** riassembleds = calloc(1, sizeof(char*)); char* charLen = malloc(sizeof(char)); riassembleds[0] = charLen; int riassembledLen = 1; int stringalen = lenstring(stringa); char tempstring[stringalen]; strcpy(tempstring, stringa); for (int i = 0; i < stringalen; i++){ for (int j = 0; j < stringalen; j++){ tempstring[i] = stringa[j]; tempstring[j] = stringa[i]; //printf("%s\n", tempstring); if (isStringInArray(tempstring, riassembleds, riassembledLen) == 0){ riassembleds = realloc(riassembleds, (riassembledLen+1)*sizeof(char*)); riassembledLen++; riassembleds[riassembledLen-1] = calloc(stringalen, sizeof(char)); printf("%p\n", riassembleds[riassembledLen-1]); strcpy(riassembleds[riassembledLen-1], tempstring); } strcpy(tempstring, stringa); } } *charLen = (char)riassembledLen; riassembleds[0] = charLen; /*return the array with the length of the it casted into a char pointer as the first element*/ return riassembleds; } int main(int argc, char *argv[]){ char** array = riassemble("ciao"); int arraylen = (int)(*(array[0])); printf("\n%d\n", arraylen); printStrArray(array, arraylen); for (int i=0; i<arraylen; i++) { free(array[i]); } free(array); return 0; }
崩溃发生在释放由以下代码分配的第二个元素:
riassembleds = realloc(riassembleds, (riassembledLen+1)*sizeof(char*)); riassembledLen++; riassembleds[riassembledLen-1] = calloc(stringalen, sizeof(char)); printf("%p\n", riassembleds[riassembledLen-1]); strcpy(riassembleds[riassembledLen-1], tempstring);
问题根源分析
临时字符串数组越界
tempstring定义为char tempstring[stringalen];,但stringalen是输入字符串的长度(如"ciao"为4),strcpy需要存储末尾的'\0',会导致越界写入,破坏堆内存结构,后续free时触发崩溃。字符串存储空间分配不足
calloc(stringalen, sizeof(char))仅分配了stringalen个字符空间,同样没有预留'\0'的位置,strcpy写入时越界,破坏堆结构。字符串存在性检查逻辑错误
isStringInArray函数从i=0开始遍历数组,但riassembleds[0]是存储长度的char*,不是字符串,将其传入strcmp会访问非法内存,引发未定义行为,进一步破坏堆。数组长度统计逻辑错误
初始riassembledLen=1(对应存储长度的元素),但添加新元素时,isStringInArray会错误地将这个长度元素当作字符串检查,导致逻辑混乱,可能重复添加或错误修改内存。
修复方案
针对上述问题,逐一修复:
1. 修正临时字符串空间大小
将tempstring的定义改为:
char tempstring[stringalen + 1];
2. 修正字符串存储空间分配
将calloc的参数改为:
riassembleds[riassembledLen-1] = calloc(stringalen + 1, sizeof(char));
3. 修正字符串存在性检查的遍历范围
修改isStringInArray函数,从i=1开始遍历(跳过首元素的长度指针):
int isStringInArray(char* string, char** stringArray, int arrayLen){ // 跳过首元素的长度指针,从第1个字符串开始检查 for (int i = 1; i < arrayLen; i++){ if (strcmp(string, stringArray[i]) == 0){ return 1; } } return 0; }
4. 修正初始数组长度的逻辑
在riassemble函数中,初始时riassembledLen应该代表字符串元素的数量,而非总数组元素数。调整初始逻辑:
char** riassembleds = calloc(1, sizeof(char*)); char* charLen = malloc(sizeof(char)); riassembleds[0] = charLen; // 初始时字符串元素数量为0,总数组元素数为1(长度指针) int riassembledStrCount = 0; int stringalen = lenstring(stringa); char tempstring[stringalen + 1]; strcpy(tempstring, stringa);
后续添加元素时的逻辑调整为:
if (isStringInArray(tempstring, riassembleds, riassembledStrCount + 1) == 0){ riassembleds = realloc(riassembleds, (riassembledStrCount + 2)*sizeof(char*)); riassembledStrCount++; riassembleds[riassembledStrCount] = calloc(stringalen + 1, sizeof(char)); strcpy(riassembleds[riassembledStrCount], tempstring); }
最后设置长度时:
// 总数组元素数是字符串数量+1(长度指针) *charLen = (char)(riassembledStrCount + 1);
修复后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> void printStrArray(char** array, int arraysize){ int j = 0; // 跳过首元素的长度指针,从第1个字符串开始打印 for (int i = 1; i < arraysize; i++){ j = 0; while (array[i][j] != '\0'){ printf("%c", array[i][j]); j++; } printf("\n"); } } int isStringInArray(char* string, char** stringArray, int arrayLen){ // 跳过首元素的长度指针 for (int i = 1; i < arrayLen; i++){ if (strcmp(string, stringArray[i]) == 0){ return 1; } } return 0; } int lenstring(char* string){ char currchar = string[0]; int strlen = 0; while (currchar != '\0'){ strlen++; currchar = string[strlen]; } return strlen; } char** riassemble(char* stringa){ char** riassembleds = calloc(1, sizeof(char*)); char* charLen = malloc(sizeof(char)); riassembleds[0] = charLen; int riassembledStrCount = 0; int stringalen = lenstring(stringa); char tempstring[stringalen + 1]; strcpy(tempstring, stringa); for (int i = 0; i < stringalen; i++){ for (int j = 0; j < stringalen; j++){ tempstring[i] = stringa[j]; tempstring[j] = stringa[i]; // 总数组长度是字符串数量+1(长度指针) if (isStringInArray(tempstring, riassembleds, riassembledStrCount + 1) == 0){ riassembleds = realloc(riassembleds, (riassembledStrCount + 2)*sizeof(char*)); riassembledStrCount++; riassembleds[riassembledStrCount] = calloc(stringalen + 1, sizeof(char)); strcpy(riassembleds[riassembledStrCount], tempstring); } strcpy(tempstring, stringa); } } // 总数组元素数:字符串数+1 *charLen = (char)(riassembledStrCount + 1); return riassembleds; } int main(int argc, char *argv[]){ char** array = riassemble("ciao"); int arraylen = (int)(*(array[0])); printf("\nTotal array elements: %d\n", arraylen); printf("Strings in array:\n"); printStrArray(array, arraylen); // 释放所有元素 for (int i=0; i<arraylen; i++) { free(array[i]); } free(array); return 0; }
额外建议
- 避免用
char存储数组长度:char的取值范围有限(-128~127),如果生成的字符串数量超过这个范围会溢出,建议改用int类型存储长度。 - 检查内存分配结果:
calloc、malloc、realloc可能返回NULL,添加检查逻辑可以避免空指针访问。 - 使用标准库函数:
lenstring可以直接用标准库的strlen替代,减少自定义函数的潜在错误。
内容的提问来源于stack exchange,提问作者Patientes

