C语言ncurses贪吃蛇:静态数组正常,动态数组存坐标失效求助
问题排查与修复
核心问题1:动态数组未初始化关键元素
malloc分配的堆内存不会自动初始化,存储的是随机垃圾值。你的动态数组版本中,初始蛇头位置没有提前存入snakeTailXPositions和snakeTailYPositions的第0位,且当蛇吃到苹果后snakeTailSize立即自增,后续渲染蛇尾时会访问未赋值的数组索引(比如snakeTailSize=2时访问索引1),这些垃圾值会导致坐标异常。
而静态数组版本依赖栈内存的默认初始值(通常为0或未被覆盖的栈数据),刚好在屏幕可见范围内,因此未触发明显异常,但本质上也是不规范的写法。
修复方法:在动态数组分配完成后,立即将初始蛇头位置存入数组:
int* snakeTailXPositions = malloc(sizeof(int) * 1000); int* snakeTailYPositions = malloc(sizeof(int) * 1000); // 初始化初始蛇头位置到数组第0位 snakeTailXPositions[0] = x; snakeTailYPositions[0] = y;
核心问题2:逻辑判断误用位运算符
苹果碰撞检测的条件中,你使用了位运算符&而非逻辑运算符&&:
if (x == apples.xCoordinates[i] & y == apples.yCoordinates[i])
位运算&会对两个条件的结果做二进制位运算,而非逻辑上的“与”判断,可能导致错误判定(比如x相等但y不相等时,位运算结果可能不为0,错误触发吃苹果逻辑)。
修复方法:替换为逻辑运算符&&:
if (x == apples.xCoordinates[i] && y == apples.yCoordinates[i])
修复后的完整动态数组代码
#include <ncurses.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #define DELAY 100000 #define APPLECOUNT 10 void preventAppleOverlap(int* appleX, int* appleY, int xBound, int yBound); struct applePositions { int xCoordinates[APPLECOUNT]; int yCoordinates[APPLECOUNT]; }; int main(void) { int ch; int x = 1, y = 1; int maxY = 0, maxX = 0; char controls[4] = {'d', 'a', 's', 'w'}; char controlsInverted[4] = {'a', 'd', 'w', 's'}; int heading = 0; struct applePositions apples; int snakeTailSize = 1; int* snakeTailXPositions = malloc(sizeof(int) * 1000); int* snakeTailYPositions = malloc(sizeof(int) * 1000); // 初始化初始蛇头位置 snakeTailXPositions[0] = x; snakeTailYPositions[0] = y; initscr(); noecho(); curs_set(FALSE); nodelay(stdscr, TRUE); getmaxyx(stdscr, maxY, maxX); // 生成苹果初始位置 for (int i = 0; i < APPLECOUNT; i++) { apples.xCoordinates[i] = rand() % maxX; apples.yCoordinates[i] = rand() % maxY; } preventAppleOverlap(apples.xCoordinates, apples.yCoordinates, maxX, maxY); while (1) { ch = getch(); clear(); char* score = malloc(sizeof(char) * 100); sprintf(score, "%i", snakeTailSize - 1); // 渲染分数 mvprintw(0, 0, score); // 渲染蛇头 mvprintw(y, x, "#"); // 渲染蛇身 for (int i = 1; i < snakeTailSize; ++i) { mvprintw(snakeTailYPositions[i], snakeTailXPositions[i], "#"); } // 渲染苹果并处理吃苹果逻辑 for (int i = 0; i < APPLECOUNT; ++i) { // 修复位运算符为逻辑运算符 if (x == apples.xCoordinates[i] && y == apples.yCoordinates[i]) { apples.xCoordinates[i] = -1; apples.yCoordinates[i] = -1; snakeTailSize++; } mvprintw(apples.yCoordinates[i], apples.xCoordinates[i], "@"); } refresh(); usleep(DELAY); // 输入处理 int previousHeading = heading; for (int i = 0; i < 4; ++i) { if (controls[i] == ch) { heading = i; } } // 禁止180度掉头 if (controls[heading] == controlsInverted[previousHeading]) { heading = previousHeading; } switch (heading) { case 0: x += 1; break; case 1: x -= 1; break; case 2: y += 1; break; case 3: y -= 1; break; default: break; } // 蛇身移动逻辑 int* snakeTailXPosTemp = malloc(sizeof(int) * snakeTailSize); int* snakeTailYPosTemp = malloc(sizeof(int) * snakeTailSize); for (int i = 0; i < snakeTailSize; ++i) { snakeTailXPosTemp[i] = snakeTailXPositions[i]; snakeTailYPosTemp[i] = snakeTailYPositions[i]; } snakeTailXPositions[0] = x; snakeTailYPositions[0] = y; for (int i = 0; i < snakeTailSize - 1; ++i) { snakeTailXPositions[i + 1] = snakeTailXPosTemp[i]; snakeTailYPositions[i + 1] = snakeTailYPosTemp[i]; } // 重生被吃掉的苹果 for (int i = 0; i < APPLECOUNT; ++i) { if (apples.xCoordinates[i] == -1) { apples.xCoordinates[i] = rand() % maxX; apples.yCoordinates[i] = rand() % maxY; } } preventAppleOverlap(apples.xCoordinates, apples.yCoordinates, maxX, maxY); // 蛇身碰撞检测 for (int i = 0; i < snakeTailSize; ++i) { for (int j = 0; j < snakeTailSize; ++j) { if (snakeTailXPositions[i] == snakeTailXPositions[j] && snakeTailYPositions[i] == snakeTailYPositions[j] && i != j) { free(snakeTailXPositions); free(snakeTailYPositions); endwin(); return 0; } } } // 边界碰撞检测 if (x >= maxX || x < 0 || y >= maxY || y < 0) { free(snakeTailXPositions); free(snakeTailYPositions); endwin(); return 0; } free(score); free(snakeTailXPosTemp); free(snakeTailYPosTemp); } endwin(); return 0; } void preventAppleOverlap(int* appleX, int* appleY, int xBound, int yBound) { int cycleCount = 1; int noOverlap = 0; while (!noOverlap) { cycleCount++; noOverlap = 1; for (int i = 0; i < APPLECOUNT; ++i) { for (int j = 0; j < APPLECOUNT; ++j) { if (appleX[i] == appleX[j] && appleY[i] == appleY[j] && i != j) { noOverlap = 0; appleX[i] = rand() % xBound; appleY[i] = rand() % yBound; } } } if (cycleCount == 2) { break; } } }
额外优化建议
- 添加
malloc失败检查,避免空指针访问:if (!snakeTailXPositions || !snakeTailYPositions) { fprintf(stderr, "内存分配失败\n"); exit(EXIT_FAILURE); } - 优化蛇身碰撞检测逻辑,只需检查蛇头与身体的碰撞,减少重复判断:
// 仅检查蛇头是否与身体碰撞 for (int i = 1; i < snakeTailSize; ++i) { if (snakeTailXPositions[0] == snakeTailXPositions[i] && snakeTailYPositions[0] == snakeTailYPositions[i]) { free(snakeTailXPositions); free(snakeTailYPositions); endwin(); return 0; } }
内容的提问来源于stack exchange,提问作者83649s
相关产品推荐
相关产品推荐

