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

迷宫求解器中重分配动态数组的赋值异常问题排查

问题描述

在使用realloc()重分配动态数组后,存储值出现异常。开发迷宫求解器时,用struct tile类型的一维数组tile_array作为迷宫的内部表示,每个元素对应输入文件中的一个字符,结构体包含字符、距离、行列等附加数据。迷宫由包含该数组的struct maze表示,因输入文件长度未知,需动态分配内存。循环中创建struct tile并赋值到数组末尾的步骤正常,但调用realloc()扩容后,后续存储元素时printf()输出的所有参数均异常,无法定位问题。

原代码

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>

struct tile
{
    // should be one element in tile array
    bool visited;
    char type;              // '#' / ' ' / 'X' / 'o'
    size_t distance;        // distance in tile_array
    size_t row;
    size_t col;
    struct tile* previous;  // pointer to previous tile
};

struct maze
{
    struct tile* tile_array;    // 1D array of <tile> structs
    size_t length;              // length of tile_array
    size_t *rows_lengths;          // [row1_len, row2_len, ...]
    size_t row_count;           // count of rows used
};

// 假设已实现的非法字符检查函数
bool illegal_input_char(char c) {
    return !(c == '#' || c == ' ' || c == 'X' || c == 'o' || c == '\n');
}

bool maze_create(struct maze *maze, FILE *file)
{
    size_t array_length = 4;

    // allocate tile_array
    struct tile *tile_array = malloc(array_length * sizeof(struct tile));
    if (tile_array == NULL) {
        return false;
    }

    // allocate rows_lengths array
    size_t rows_lengths_count = 4;
    size_t *rows_lengths = malloc(rows_lengths_count * sizeof(size_t));
    if (rows_lengths == NULL) {
        free(tile_array);
        tile_array = NULL;
        return false;
    }

    char curr_char;
    size_t curr_distance = 0;
    size_t curr_row = 0;
    size_t curr_col = 0;
    struct tile *previous = NULL;
    while ((curr_char = fgetc(file)) != EOF) {
        assert(curr_distance <= array_length);
        assert(curr_row <= rows_lengths_count);

        // allocate more memory for tile_array
        if (curr_distance == array_length) {
            array_length += 4;
            struct tile *tmp = realloc(tile_array, (array_length * sizeof(struct tile)));
            if (tmp == NULL) {
                free(tile_array);
                tile_array = NULL;
                free(rows_lengths);
                rows_lengths = NULL;
                return false;
            }
            tile_array = tmp;
            tmp = NULL;
        }

        // allocate more memory for rows_lengths
        if (rows_lengths_count == curr_row) {
            rows_lengths_count += 4;
            size_t *tmp = realloc(rows_lengths, (rows_lengths_count * sizeof(size_t)));
            if (tmp == NULL) {
                free(tile_array);
                tile_array = NULL;
                free(rows_lengths);
                rows_lengths = NULL;
                return false;
            }
            rows_lengths = tmp;
            tmp = NULL;
        }

        if (curr_char == '\n') {
        // set variables & continue
        }
        if (illegal_input_char(curr_char)) {
        // invalid input, free memory & return false
        }

        // create new tile and insert to the maze
        struct tile curr_tile = {false, curr_char, curr_distance, curr_row, curr_col, previous};
        tile_array[curr_distance] = curr_tile;

        printf("WRITTEN CHAR: '%lu', direction:%2lu", ((*maze).tile_array[curr_distance]).type, ((*maze).tile_array[curr_distance]).distance);

        curr_col++;
        curr_distance++;
        previous = &curr_tile;
    }

    (*maze).length = curr_distance;
    (*maze).row_count = curr_row - 1;
    return true;
}
问题分析与修复

核心问题1:未关联动态数组到maze结构体

函数内部分配了tile_array和rows_lengths,但始终未将这两个指针赋值给*maze的对应成员。printf中访问(*maze).tile_array[curr_distance]时,(*maze).tile_array是未初始化的野指针,直接触发未定义行为,导致输出异常。

核心问题2:previous指针指向栈临时变量

每次循环创建的struct tile curr_tile是栈上局部变量,循环结束后内存会被回收。previous = &curr_tile会让结构体中的previous指针指向无效内存,后续使用会引发错误。

核心问题3:printf格式化符类型不匹配

type是char类型,却用了%lu(无符号长整型)格式化符;distance是size_t类型,对应格式化符应为%zu,类型不匹配导致输出异常。

修复后的代码

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>

struct tile
{
    bool visited;
    char type;              // '#' / ' ' / 'X' / 'o'
    size_t distance;        // distance in tile_array
    size_t row;
    size_t col;
    struct tile* previous;  // pointer to previous tile
};

struct maze
{
    struct tile* tile_array;    // 1D array of <tile> structs
    size_t length;              // length of tile_array
    size_t *rows_lengths;       // [row1_len, row2_len, ...]
    size_t row_count;           // count of rows used
};

bool illegal_input_char(char c) {
    return !(c == '#' || c == ' ' || c == 'X' || c == 'o' || c == '\n');
}

bool maze_create(struct maze *maze, FILE *file)
{
    size_t array_length = 4;
    // 直接将分配的数组关联到maze结构体
    maze->tile_array = malloc(array_length * sizeof(struct tile));
    if (maze->tile_array == NULL) {
        return false;
    }

    size_t rows_lengths_count = 4;
    maze->rows_lengths = malloc(rows_lengths_count * sizeof(size_t));
    if (maze->rows_lengths == NULL) {
        free(maze->tile_array);
        maze->tile_array = NULL;
        return false;
    }

    char curr_char;
    size_t curr_distance = 0;
    size_t curr_row = 0;
    size_t curr_col = 0;
    struct tile *previous = NULL;
    while ((curr_char = fgetc(file)) != EOF) {
        assert(curr_distance <= array_length);
        assert(curr_row <= rows_lengths_count);

        // 扩容tile_array,直接操作maze的成员
        if (curr_distance == array_length) {
            array_length += 4;
            struct tile *tmp = realloc(maze->tile_array, array_length * sizeof(struct tile));
            if (tmp == NULL) {
                free(maze->tile_array);
                maze->tile_array = NULL;
                free(maze->rows_lengths);
                maze->rows_lengths = NULL;
                return false;
            }
            maze->tile_array = tmp;
        }

        // 扩容rows_lengths,直接操作maze的成员
        if (rows_lengths_count == curr_row) {
            rows_lengths_count += 4;
            size_t *tmp = realloc(maze->rows_lengths, rows_lengths_count * sizeof(size_t));
            if (tmp == NULL) {
                free(maze->tile_array);
                maze->tile_array = NULL;
                free(maze->rows_lengths);
                maze->rows_lengths = NULL;
                return false;
            }
            maze->rows_lengths = tmp;
        }

        // 处理换行逻辑
        if (curr_char == '\n') {
            maze->rows_lengths[curr_row] = curr_col;
            curr_col = 0;
            curr_row++;
            previous = NULL;
            continue;
        }

        // 非法字符检查
        if (illegal_input_char(curr_char)) {
            free(maze->tile_array);
            maze->tile_array = NULL;
            free(maze->rows_lengths);
            maze->rows_lengths = NULL;
            return false;
        }

        // 直接初始化数组中的tile,避免临时变量问题
        maze->tile_array[curr_distance].visited = false;
        maze->tile_array[curr_distance].type = curr_char;
        maze->tile_array[curr_distance].distance = curr_distance;
        maze->tile_array[curr_distance].row = curr_row;
        maze->tile_array[curr_distance].col = curr_col;
        maze->tile_array[curr_distance].previous = previous;

        // 使用正确的格式化符
        printf("WRITTEN CHAR: '%c', distance:%zu\n", 
               maze->tile_array[curr_distance].type, 
               maze->tile_array[curr_distance].distance);

        curr_col++;
        // 将previous指向数组中有效的tile地址
        previous = &maze->tile_array[curr_distance];
        curr_distance++;
    }

    // 处理文件末尾无换行的情况
    if (curr_col > 0) {
        maze->rows_lengths[curr_row] = curr_col;
        curr_row++;
    }

    maze->length = curr_distance;
    maze->row_count = curr_row;
    return true;
}

额外注意事项

  • 确保illegal_input_char函数正确过滤非法字符;
  • 处理文件末尾无换行的场景,避免最后一行长度未被记录;
  • 调试用的assert在发布版本建议替换为显式错误检查。

内容的提问来源于stack exchange,提问作者Loosos

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 23:12:06