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
相关产品推荐
相关产品推荐

