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

Segmentation fault问题排查:paint函数触发未初始化值与非法写入

解决C++程序中的Segmentation Fault及Valgrind检测到的未初始化值/非法写入问题

我运行以下C++程序时持续出现Segmentation fault (core dumped)错误。通过Valgrind检测发现,paint函数存在未初始化值使用错误(涉及8字节值,发生在4023AF行)以及Invalid write of size 1错误(同样在4023AF行,程序尝试向地址0x5写入1字节)。Valgrind未检测到内存泄漏,但我无法定位问题所在。

相关代码如下:

#include <iostream>
#include <fstream>
#include <string>

struct TextGrid {
    char         **tiles;
    unsigned int   height;
    unsigned int   width;
};

void paint(TextGrid& grid, unsigned int x, unsigned int y, char fill) {
    if (x < grid.width && y < grid.height) {
        grid.tiles[y][x] = fill;
    }
}
void resize(TextGrid& grid, unsigned int width, unsigned int height, char fill) {
    char **newTiles = new char*[height];
    for (unsigned int i = 0; i < height; ++i) {
        newTiles[i] = new char[width];
        for (unsigned int j = 0; j < width; ++j) {
            if (i < grid.height && j < grid.width) {
                newTiles[i][j] = grid.tiles[i][j];
            }
            else {
                newTiles[i][j] = fill;
            }
        }
    }

    for (unsigned int i = 0; i < grid.height; ++i) {
        delete[] grid.tiles[i];
    }
    delete[] grid.tiles;

    grid.tiles = newTiles;
    grid.width = width;
    grid.height = height;
}

void transform(TextGrid& grid, char target, char replacement) {
    for (unsigned int i = 0; i < grid.height; ++i) {
        for (unsigned int j = 0; j < grid.width; ++j) {
            if (grid.tiles[i][j] == target) {
                grid.tiles[i][j] = replacement;
            }
        }
    }
}

TextGrid load_grid(std::string file_path) {
    std::ifstream infile(file_path);
    std::string line;
    std::getline(infile, line);
    unsigned int height = 1;
    unsigned int width = line.length();
    while (std::getline(infile, line)) {
        ++height;
    }
    infile.clear();
    infile.seekg(0, std::ios::beg);

    TextGrid grid;
    grid.tiles = new char*[height];
    grid.height = height;
    grid.width = width;
    for (unsigned int i = 0; i < height; ++i) {
        grid.tiles[i] = new char[width];
        std::getline(infile, line);
        for (unsigned int j = 0; j < width; ++j) {
             grid.tiles[i][j] = line[j];
        }
    }
    infile.close();
    return grid;
}

void save_grid(TextGrid& grid, std::string file_path) {
    std::ofstream outfile(file_path);
    for (unsigned int i = 0; i < grid.height; ++i) {
        for (unsigned int j = 0; j < grid.width; ++j) {
            outfile << grid.tiles[i][j];
        }
        outfile << '\n';
    }
    outfile.close();
}

void show(TextGrid& grid) {
    for (unsigned int i = 0; i < grid.height; ++i) {
        for (unsigned int j = 0; j < grid.width; ++j) {
            std::cout << grid.tiles[i][j];
        }
        std::cout << '\n';
    }
}

int main() {
    TextGrid grid;
    grid.width = 16;
    grid.height = 8;
    grid.tiles = new char*[grid.height];
    for (unsigned int i = 0; i < grid.height; ++i) {
        grid.tiles[i] = new char[grid.width];
        for (unsigned int j = 0; j < grid.width; ++j) {
            grid.tiles[i][j] = ' '; 
        }
        paint(grid, 5, 2, 'X');
    }
    resize(grid, 20, 10, '.');
    transform(grid, 'X', 'O');
    save_grid(grid, "output.txt");
    TextGrid loaded_grid = load_grid("output.txt");
    show(loaded_grid);

    for (unsigned int i = 0; i < grid.height; ++i) {
        delete[] grid.tiles[i];
    }
    delete[] grid.tiles;

    for (unsigned int i = 0; i < loaded_grid.height; ++i) {
         delete[] loaded_grid.tiles[i];
    }
    delete[] loaded_grid.tiles;

    return 0;
}

问题定位与修复

1. 核心错误来源:main函数中paint调用时机错误

在初始化grid.tiles的循环里,每次迭代仅分配grid.tiles[i],但你在循环内调用paint(grid,5,2,'X')——这意味着当i=0或i=1时,grid.tiles[2]还未被分配,是未初始化的野指针。此时访问grid.tiles[2][5]会触发未初始化值使用和非法写入,直接导致段错误。

修复方式:将paint调用移到初始化循环外部,确保所有行的内存都已分配完成:

int main() {
    TextGrid grid;
    grid.width = 16;
    grid.height = 8;
    grid.tiles = new char*[grid.height];
    for (unsigned int i = 0; i < grid.height; ++i) {
        grid.tiles[i] = new char[grid.width];
        for (unsigned int j = 0; j < grid.width; ++j) {
            grid.tiles[i][j] = ' '; 
        }
    }
    // 移到循环外,确保所有行内存已分配
    paint(grid, 5, 2, 'X');
    resize(grid, 20, 10, '.');
    // 后续代码不变
}

2. load_grid函数的鲁棒性问题

  • 如果文件为空或打开失败,std::getline会直接失败,导致width被初始化为0,后续内存分配和访问会出错。
  • 若文件中每行长度不一致,直接通过line[j]访问会触发越界。

修复方式:添加文件打开校验,处理行长度不一致的情况:

TextGrid load_grid(std::string file_path) {
    std::ifstream infile(file_path);
    if (!infile.is_open()) {
        // 返回空网格避免后续错误
        TextGrid empty{};
        return empty;
    }
    std::string line;
    unsigned int height = 0;
    unsigned int width = 0;
    // 先遍历一次文件,记录高度和最大宽度
    while (std::getline(infile, line)) {
        ++height;
        if (line.length() > width) {
            width = line.length();
        }
    }
    infile.clear();
    infile.seekg(0, std::ios::beg);

    TextGrid grid;
    grid.tiles = new char*[height];
    grid.height = height;
    grid.width = width;
    for (unsigned int i = 0; i < height; ++i) {
        grid.tiles[i] = new char[width];
        std::getline(infile, line);
        for (unsigned int j = 0; j < width; ++j) {
            // 行长度不足时用空格填充
            grid.tiles[i][j] = (j < line.length()) ? line[j] : ' ';
        }
    }
    infile.close();
    return grid;
}

3. 额外优化:给TextGrid添加构造/析构函数

手动管理二维数组内存容易出错,添加构造、析构函数可以自动处理内存分配与释放,避免泄漏或野指针:

struct TextGrid {
    char         **tiles = nullptr;
    unsigned int   height = 0;
    unsigned int   width = 0;

    // 默认构造
    TextGrid() = default;
    // 带参数构造,初始化网格
    TextGrid(unsigned int w, unsigned int h, char fill = ' ') : width(w), height(h) {
        tiles = new char*[height];
        for (unsigned int i = 0; i < height; ++i) {
            tiles[i] = new char[width];
            for (unsigned int j = 0; j < width; ++j) {
                tiles[i][j] = fill;
            }
        }
    }

    // 析构函数自动释放内存
    ~TextGrid() {
        for (unsigned int i = 0; i < height; ++i) {
            delete[] tiles[i];
        }
        delete[] tiles;
        tiles = nullptr;
        height = width = 0;
    }

    // 禁用拷贝构造和赋值,避免浅拷贝
    TextGrid(const TextGrid&) = delete;
    TextGrid& operator=(const TextGrid&) = delete;

    // 添加移动构造和赋值,支持所有权转移
    TextGrid(TextGrid&& other) noexcept {
        tiles = other.tiles;
        height = other.height;
        width = other.width;
        other.tiles = nullptr;
        other.height = other.width = 0;
    }

    TextGrid& operator=(TextGrid&& other) noexcept {
        if (this != &other) {
            // 释放当前资源
            for (unsigned int i = 0; i < height; ++i) {
                delete[] tiles[i];
            }
            delete[] tiles;
            // 接管对方资源
            tiles = other.tiles;
            height = other.height;
            width = other.width;
            other.tiles = nullptr;
            other.height = other.width = 0;
        }
        return *this;
    }
};

优化后main函数的初始化可以简化为:TextGrid grid(16,8,' ');,无需手动分配内存。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 00:15:11