CLion正常运行的MC克隆代码在VS中触发访问违例错误排查
类Minecraft区块生成代码在VS中触发访问违例的解决方案
问题描述
我编写了一款类Minecraft的区块生成代码,基于正弦波实现程序化世界生成,在CLion中可正常运行,能通过字符(0=#、1=空格、2=~)展示区块切片;但在Visual Studio中运行时,使用World类会触发0xC0000005访问违例错误(错误地址0x0000000000000BFA,位于第83行);直接使用Chunk类时,代码执行完毕后也会触发相同错误,怀疑问题与析构函数相关,完整代码如下:
#include<D:\glfw-3.3.8\glfw-3.3.8\include\GLFW\glfw3.h> #include<D:\glm-master\glm\glm.hpp> #include<D:\FastNoiseLite-master\Cpp\FastNoiseLite.h> #include<iostream> #include<vector> const int xMax = 32; const int yMax = 384; const int zMax = 32; enum BlockType { Stone = 0, Air = 1, Water = 2, Dirt = 3, Grass = 4 }; BlockType getBlock(int x, int y, int z) { double frequency = 0.2; double amplitude = 10; double xOffset = sin(x * (frequency)) * (amplitude - 5) + sin(x * (frequency - 0.1)) * (amplitude + 5) + sin(x * (frequency + 0.1)) * amplitude; double zOffset = sin(z * (frequency)) * (amplitude - 4) + sin(z * (frequency - 0.06)) * (amplitude + 5) + sin(z * (frequency + 0.05)) * amplitude + 2; double surfaceY = 100 + xOffset + zOffset; /*FastNoiseLite noise; noise.SetNoiseType(noise.NoiseType_Perlin); noise.SetFractalOctaves(4); double surfaceY = 100 + noise.GetNoise(float(x), float(z)) * 20 + noise.GetNoise(float(x*0.2), float(z*0.35)) * 10;*/ double waterLevel = 125; return (y < surfaceY) ? Stone :(y < waterLevel) ? Water : Air; } class Array3D { int*** data; int numberOfRows; int numberOfColumns; int numberOfAisles; public: Array3D(int rows, int columns, int aisles) : numberOfRows(rows), numberOfColumns(columns), numberOfAisles(aisles) { data = new int** [rows]; for (int i = 0; i < rows; i++) { data[i] = new int* [columns]; for (int j = 0; j < columns; j++) { data[i][j] = new int[aisles]; } } } ~Array3D() { for (int i = 0; i < numberOfRows; i++) { for (int j = 0; j < numberOfColumns; j++) { delete[] data[i][j]; } delete[] data[i]; } delete[] data; } void Set(int row, int column, int aisle, int value) { data[row][column][aisle] = value; } int Get(int row, int column, int aisle) { return data[row][column][aisle]; } }; class Chunk { Array3D data; int cornerBlockX; int cornerBlockZ; public: Chunk() : cornerBlockX(0), cornerBlockZ(0), data(xMax, yMax, zMax){} Chunk(int cornerBlockX, int cornerBlockZ) : cornerBlockX(cornerBlockX), cornerBlockZ(cornerBlockZ), data(xMax, yMax, zMax) { for (int i = 0; i < xMax; i++) for (int j = 0; j < yMax; j++) for (int k = 0; k < zMax; k++) { BlockType block = getBlock(cornerBlockX + i, j, cornerBlockZ + k); data.Set(i, j, k, block); } } int Get(int row, int column, int aisle) { return data.Get(row, column, aisle); } }; class World { Chunk** data; int numberOfRows; int numberOfColumns; public: World(int rows, int columns) : numberOfRows(rows), numberOfColumns(columns), data(new Chunk* [rows]) { for (int i = 0; i < rows; i++) { data[i] = new Chunk[columns]; for (int j = 0; j < columns; j++) { data[i][j] = Chunk(i * xMax, j * zMax); } } } ~World() { for (int i = 0; i < numberOfRows; i++) { delete[] data[i]; } delete[] data; } Chunk GetChunk(int row, int column) { return data[row][column]; } }; void PrintChunk2D(Chunk array, int zSlice) { int lines = 0; for (int i = yMax - 1; i > -1; i--) { for (int j = 0; j < xMax; j++) { if (array.Get(j, i, zSlice) == 0) std::cout << '#'; else if (array.Get(j, i, zSlice) == 2) std::cout << '~'; else if (array.Get(j, i, zSlice) == 1) std::cout << ' '; else std::cout << '@'; } std::cout << '\n'; lines++; } std::cout << lines; } int main() { int chunkX = 0; int chunkZ = 0; int cornerBlockX = xMax * chunkX; int cornerBlockZ = zMax * chunkZ; //Error ? in VS only!? Destructor? int worldSize[2] = { 2, 2 }; World world(worldSize[0], worldSize[1]); PrintChunk2D(world.GetChunk(chunkX, chunkZ), 0); //Chunk chunk(cornerBlockX, cornerBlockZ); //PrintChunk2D(chunk, 0); }
问题根源
- 浅拷贝导致重复释放内存:
Array3D类手动管理动态内存,但未显式定义拷贝构造函数和赋值运算符,编译器生成的默认版本只会拷贝指针地址,导致多个对象共享同一块内存。当这些对象被析构时,会重复释放同一块内存,触发访问违例。 - 对象拷贝场景过多:
World构造中通过赋值操作创建Chunk对象、GetChunk返回值传值、PrintChunk2D参数传值,都会触发对象拷贝,进而触发Array3D的浅拷贝。
修复方案
- 为
Array3D实现深拷贝的拷贝构造函数和赋值运算符,避免内存共享; - 修改
World的初始化逻辑,直接构造Chunk对象而非先默认构造再赋值; - 修改函数参数和返回值为引用,减少不必要的对象拷贝。
修复后的完整代码
#include<D:\glfw-3.3.8\glfw-3.3.8\include\GLFW\glfw3.h> #include<D:\glm-master\glm\glm.hpp> #include<D:\FastNoiseLite-master\Cpp\FastNoiseLite.h> #include<iostream> #include<vector> const int xMax = 32; const int yMax = 384; const int zMax = 32; enum BlockType { Stone = 0, Air = 1, Water = 2, Dirt = 3, Grass = 4 }; BlockType getBlock(int x, int y, int z) { double frequency = 0.2; double amplitude = 10; double xOffset = sin(x * (frequency)) * (amplitude - 5) + sin(x * (frequency - 0.1)) * (amplitude + 5) + sin(x * (frequency + 0.1)) * amplitude; double zOffset = sin(z * (frequency)) * (amplitude - 4) + sin(z * (frequency - 0.06)) * (amplitude + 5) + sin(z * (frequency + 0.05)) * amplitude + 2; double surfaceY = 100 + xOffset + zOffset; double waterLevel = 125; return (y < surfaceY) ? Stone :(y < waterLevel) ? Water : Air; } class Array3D { int*** data; int numberOfRows; int numberOfColumns; int numberOfAisles; public: Array3D(int rows, int columns, int aisles) : numberOfRows(rows), numberOfColumns(columns), numberOfAisles(aisles) { data = new int** [rows]; for (int i = 0; i < rows; i++) { data[i] = new int* [columns]; for (int j = 0; j < columns; j++) { data[i][j] = new int[aisles]; } } } // 拷贝构造函数:深拷贝 Array3D(const Array3D& other) : numberOfRows(other.numberOfRows), numberOfColumns(other.numberOfColumns), numberOfAisles(other.numberOfAisles) { data = new int** [numberOfRows]; for (int i = 0; i < numberOfRows; i++) { data[i] = new int* [numberOfColumns]; for (int j = 0; j < numberOfColumns; j++) { data[i][j] = new int[numberOfAisles]; // 拷贝每个元素 for (int k = 0; k < numberOfAisles; k++) { data[i][j][k] = other.data[i][j][k]; } } } } // 赋值运算符:深拷贝 Array3D& operator=(const Array3D& other) { if (this == &other) return *this; // 防止自赋值 // 先释放当前内存 for (int i = 0; i < numberOfRows; i++) { for (int j = 0; j < numberOfColumns; j++) { delete[] data[i][j]; } delete[] data[i]; } delete[] data; // 分配新内存并拷贝数据 numberOfRows = other.numberOfRows; numberOfColumns = other.numberOfColumns; numberOfAisles = other.numberOfAisles; data = new int** [numberOfRows]; for (int i = 0; i < numberOfRows; i++) { data[i] = new int* [numberOfColumns]; for (int j = 0; j < numberOfColumns; j++) { data[i][j] = new int[numberOfAisles]; for (int k = 0; k < numberOfAisles; k++) { data[i][j][k] = other.data[i][j][k]; } } } return *this; } ~Array3D() { for (int i = 0; i < numberOfRows; i++) { for (int j = 0; j < numberOfColumns; j++) { delete[] data[i][j]; } delete[] data[i]; } delete[] data; } void Set(int row, int column, int aisle, int value) { data[row][column][aisle] = value; } int Get(int row, int column, int aisle) const { return data[row][column][aisle]; } }; class Chunk { Array3D data; int cornerBlockX; int cornerBlockZ; public: Chunk() : cornerBlockX(0), cornerBlockZ(0), data(xMax, yMax, zMax){} Chunk(int cornerBlockX, int cornerBlockZ) : cornerBlockX(cornerBlockX), cornerBlockZ(cornerBlockZ), data(xMax, yMax, zMax) { for (int i = 0; i < xMax; i++) for (int j = 0; j < yMax; j++) for (int k = 0; k < zMax; k++) { BlockType block = getBlock(cornerBlockX + i, j, cornerBlockZ + k); data.Set(i, j, k, block); } } int Get(int row, int column, int aisle) const { return data.Get(row, column, aisle); } }; class World { Chunk** data; int numberOfRows; int numberOfColumns; public: World(int rows, int columns) : numberOfRows(rows), numberOfColumns(columns), data(new Chunk* [rows]) { for (int i = 0; i < rows; i++) { // 分配Chunk数组内存 data[i] = static_cast<Chunk*>(operator new[](columns * sizeof(Chunk))); for (int j = 0; j < columns; j++) { // 直接在已分配内存上构造Chunk对象 new (&data[i][j]) Chunk(i * xMax, j * zMax); } } } ~World() { for (int i = 0; i < numberOfRows; i++) { // 手动调用每个Chunk的析构函数 for (int j = 0; j < numberOfColumns; j++) { data[i][j].~Chunk(); } // 释放数组内存 operator delete[](data[i]); } delete[] data; } // 返回引用,避免拷贝 const Chunk& GetChunk(int row, int column) const { return data[row][column]; } }; // 参数用const引用,避免拷贝 void PrintChunk2D(const Chunk& array, int zSlice) { int lines = 0; for (int i = yMax - 1; i > -1; i--) { for (int j = 0; j < xMax; j++) { if (array.Get(j, i, zSlice) == 0) std::cout << '#'; else if (array.Get(j, i, zSlice) == 2) std::cout << '~'; else if (array.Get(j, i, zSlice) == 1) std::cout << ' '; else std::cout << '@'; } std::cout << '\n'; lines++; } std::cout << lines; } int main() { int chunkX = 0; int chunkZ = 0; int worldSize[2] = { 2, 2 }; World world(worldSize[0], worldSize[1]); PrintChunk2D(world.GetChunk(chunkX, chunkZ), 0); //Chunk chunk(cornerBlockX, cornerBlockZ); //PrintChunk2D(chunk, 0); }
内容的提问来源于stack exchange,提问作者Artemis
相关产品推荐
相关产品推荐

