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

C语言int*内存分配与char**地图加载函数段错误排查求助

地图加载函数的段错误分析与修复

问题概述

需要实现两个核心函数:

  • nb_cols_pline:返回int*类型,存储地图每行的字符长度
  • load_map:将地图文件内容加载到char**类型的二维数组并返回

当前代码运行时触发段错误,Valgrind检测到两类问题:

  • 使用大小为8的未初始化值
  • 大小为1的无效写入

同时需要明确C语言中用malloc分配int*的正确方式。

代码错误分析

1. nb_cols_pline函数的问题

  • stat调用未做错误检查:如果stat(filepath, size)执行失败,size->st_size会是未初始化的垃圾值,后续用这个值分配buffer内存时,会触发"未初始化值"错误。
  • 行遍历逻辑失效:内层循环始终从buffer[0]开始查找换行符,从未移动到下一行的起始位置;如果文件最后一行没有换行符,会直接越界访问缓冲区,触发"无效写入"错误。
  • 重复调用nb_lines:每次调用都会重新打开文件统计行数,不仅效率低下,若nb_lines实现有问题,还会导致count数组的分配大小错误。

2. load_map函数的问题

  • 内存泄漏与冗余计算:循环中每次调用nb_cols_pline都会分配新的int*,但未释放,同时重复计算每行长度完全无意义。
  • map分配逻辑完全错误:
    • 首次分配map时,运算符优先级问题导致分配大小计算错误(需加括号保证先执行乘法);
    • 后续循环中map = malloc(...)直接覆盖了之前的二级指针,导致原分配内存泄漏,且map变为一级指针,后续map[y][x]的访问属于越界写入,是"无效写入"的核心原因。
  • 索引变量误用:内层循环错误使用nb_cols[i],实际应使用nb_cols[y],i的递增逻辑也会导致buffer访问越界。
  • 同样存在stat未检查返回值的问题。

C语言中malloc int*的正确方式

要分配一个能存储n个int的动态数组,语法如下:

int *arr = malloc(sizeof(int) * n);
  • 必须检查分配结果:如果malloc返回NULL,说明内存分配失败,需要处理该异常。
  • 使用完成后必须调用free(arr)释放内存,避免内存泄漏。

修复后的代码

修复后的nb_cols_pline

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

// 假设nb_lines是已实现的函数,返回文件总行数
int nb_lines(char *filepath);

int *nb_cols_pline(char *filepath)
{
    int x, y = 0;
    struct stat size_buf;
    // 用栈上结构体替代malloc,同时检查stat返回值
    if (stat(filepath, &size_buf) == -1) {
        perror("stat failed");
        return NULL;
    }

    int line_count = nb_lines(filepath);
    int *count = malloc(sizeof(int) * line_count);
    if (!count) {
        perror("malloc count failed");
        return NULL;
    }

    int fd = open(filepath, O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        free(count);
        return NULL;
    }

    // 多分配1字节存储字符串终止符
    char *buffer = malloc(sizeof(char) * size_buf.st_size + 1);
    if (!buffer) {
        perror("malloc buffer failed");
        close(fd);
        free(count);
        return NULL;
    }

    ssize_t bytes_read = read(fd, buffer, size_buf.st_size);
    if (bytes_read == -1) {
        perror("read failed");
        free(buffer);
        close(fd);
        free(count);
        return NULL;
    }
    buffer[bytes_read] = '\0'; // 手动添加终止符,避免越界

    char *ptr = buffer;
    for (y = 0; y < line_count; y++) {
        x = 0;
        // 遍历到换行符或字符串结束
        while (ptr[x] != '\n' && ptr[x] != '\0') {
            x++;
        }
        count[y] = x;
        // 移动指针到下一行起始位置(跳过换行符)
        if (ptr[x] == '\n') {
            ptr += x + 1;
        } else {
            // 最后一行无换行符,直接退出循环
            break;
        }
    }

    close(fd);
    free(buffer);
    return count;
}

修复后的load_map

char **load_map(char *filepath)
{
    struct stat size_buf;
    if (stat(filepath, &size_buf) == -1) {
        perror("stat failed");
        return NULL;
    }

    int line_count = nb_lines(filepath);
    int *nb_cols = nb_cols_pline(filepath);
    if (!nb_cols) {
        return NULL;
    }

    // 分配二级指针数组,+1用于存储NULL结束标记
    char **map = malloc(sizeof(char *) * (line_count + 1));
    if (!map) {
        perror("malloc map failed");
        free(nb_cols);
        return NULL;
    }

    // 为每行单独分配内存(多1字节存终止符)
    for (int x = 0; x < line_count; x++) {
        map[x] = malloc(sizeof(char) * (nb_cols[x] + 1));
        if (!map[x]) {
            perror("malloc map line failed");
            // 释放已分配的内存,避免泄漏
            for (int i = 0; i < x; i++) {
                free(map[i]);
            }
            free(map);
            free(nb_cols);
            return NULL;
        }
    }
    map[line_count] = NULL; // 设置结束标记

    int fd = open(filepath, O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        // 释放已分配内存
        for (int i = 0; i < line_count; i++) {
            free(map[i]);
        }
        free(map);
        free(nb_cols);
        return NULL;
    }

    char *buffer = malloc(sizeof(char) * size_buf.st_size + 1);
    if (!buffer) {
        perror("malloc buffer failed");
        close(fd);
        // 释放已分配内存
        for (int i = 0; i < line_count; i++) {
            free(map[i]);
        }
        free(map);
        free(nb_cols);
        return NULL;
    }

    ssize_t bytes_read = read(fd, buffer, size_buf.st_size);
    if (bytes_read == -1) {
        perror("read failed");
        free(buffer);
        close(fd);
        // 释放已分配内存
        for (int i = 0; i < line_count; i++) {
            free(map[i]);
        }
        free(map);
        free(nb_cols);
        return NULL;
    }
    buffer[bytes_read] = '\0';

    char *ptr = buffer;
    for (int y = 0; y < line_count; y++) {
        int x = 0;
        while (ptr[x] != '\n' && ptr[x] != '\0') {
            map[y][x] = ptr[x];
            x++;
        }
        map[y][x] = '\0'; // 每行末尾添加字符串终止符
        if (ptr[x] == '\n') {
            ptr += x + 1;
        } else {
            break;
        }
    }

    close(fd);
    free(buffer);
    free(nb_cols);
    return map;
}

关键修复点总结

  • 所有系统调用(stat、open、read)和malloc都添加了错误检查,避免使用无效值或空指针。
  • 修复了行遍历的指针移动逻辑,确保每次处理下一行的正确起始位置。
  • 修正了map的分配逻辑:先分配二级指针数组,再为每行单独分配内存。
  • 避免了冗余计算和内存泄漏,直接复用nb_cols_pline的返回值。
  • 为所有字符串添加终止符,避免后续操作出现未定义行为。

内容的提问来源于stack exchange,提问作者G.exe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 05:05:29