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

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的浅拷贝。

修复方案

  1. 为Array3D实现深拷贝的拷贝构造函数和赋值运算符,避免内存共享;
  2. 修改World的初始化逻辑,直接构造Chunk对象而非先默认构造再赋值;
  3. 修改函数参数和返回值为引用,减少不必要的对象拷贝。

修复后的完整代码

#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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 03:47:04