C语言中2D沙粒模拟矩阵值异常消失问题排查
问题分析与修复方案
核心问题点
- 边界检查逻辑完全错误:原代码中
if(grid[y + 1][x] <= sizeY && grid[y][x + 1] <=sizeX)是拿网格中的值(0或1)和尺寸比较,而非判断坐标是否在合法范围内。 - 越界访问内存:
drawScreen函数循环条件使用x <= sizeX和y <= sizeY,但网格索引范围是0到19(sizeX/Y=20),导致访问超出数组范围的内存,引发随机数据显示。sand函数检查下方、左下、右下位置时未做边界校验,粒子处于边缘时会访问数组外内存,破坏其他数据。
- 网格坐标遍历顺序错误:
drawScreen函数把x和y的循环顺序搞反,导致画面显示的网格是转置状态。 - 缺少画面刷新延迟:原代码没有绘制后延迟,画面刷新过快无法观察效果。
修复后的完整代码
#include <stdio.h> #include <stdlib.h> #ifdef __unix__ # include <unistd.h> #elif defined _WIN32 # include <windows.h> #define sleep(x) Sleep(1000 * (x)) #endif int sizeX = 20; int sizeY = 20; int alive = 1; int dead = 0; int grid[20][20] = {{0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}}; void drawScreen() { system("cls"); // 清屏 for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { printf(grid[y][x] == alive ? "@" : " "); } printf("\n"); } } void sand(int x, int y) { // 检查是否可以向下移动 if (y + 1 < sizeY && grid[y + 1][x] == dead) { grid[y][x] = dead; grid[y + 1][x] = alive; } // 检查是否可以向左下移动(x不能小于0) else if (x - 1 >= 0 && y + 1 < sizeY && grid[y + 1][x - 1] == dead) { grid[y][x] = dead; grid[y + 1][x - 1] = alive; } // 检查是否可以向右下移动(x不能超过边界) else if (x + 1 < sizeX && y + 1 < sizeY && grid[y + 1][x + 1] == dead) { grid[y][x] = dead; grid[y + 1][x + 1] = alive; } } int main(void) { int x, y, iterations; for (iterations = 0; iterations < 1000; iterations++) { // 从下往上遍历,避免同一帧内重复处理同一个粒子 for (y = sizeY - 2; y >= 0; y--) { for (x = 0; x < sizeX; x++) { if (grid[y][x] == alive) { sand(x, y); } } } drawScreen(); sleep(0.1); // 添加延迟,让画面可见 } return 0; }
关键修复说明
- 修正边界检查:
sand函数中每次访问相邻位置前,先判断坐标是否在0 <= x < sizeX和0 <= y < sizeY范围内。 - 修复越界遍历:
drawScreen循环条件改为y < sizeY和x < sizeX,确保只访问合法网格索引。 - 调整坐标顺序:
drawScreen先循环行(y)再循环列(x),对应grid[y][x]的存储结构,显示正确画面。 - 优化遍历顺序:主循环y从
sizeY-2开始(最底部粒子无法下移),避免无效判断。 - 添加刷新延迟:每次绘制后调用
sleep(0.1),控制画面刷新速度,便于观察沙粒下落效果。 - 修正函数返回值:
drawScreen和sand函数无需返回值,改为void类型,符合逻辑。
内容的提问来源于stack exchange,提问作者Saw
相关产品推荐
相关产品推荐

