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

C语言循环释放指针数组导致程序崩溃问题求助

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);

问题根源分析

  1. 临时字符串数组越界
    tempstring定义为char tempstring[stringalen];,但stringalen是输入字符串的长度(如"ciao"为4),strcpy需要存储末尾的'\0',会导致越界写入,破坏堆内存结构,后续free时触发崩溃。

  2. 字符串存储空间分配不足
    calloc(stringalen, sizeof(char))仅分配了stringalen个字符空间,同样没有预留'\0'的位置,strcpy写入时越界,破坏堆结构。

  3. 字符串存在性检查逻辑错误
    isStringInArray函数从i=0开始遍历数组,但riassembleds[0]是存储长度的char*,不是字符串,将其传入strcmp会访问非法内存,引发未定义行为,进一步破坏堆。

  4. 数组长度统计逻辑错误
    初始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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 05:42:07