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

运行Curses游戏代码时出现Segmentation Fault (core dumped)错误求助

Curses游戏Segmentation Fault问题排查

我正在开发一个基于Curses的Linux终端游戏,执行命令./escape 7 8 1 1 5 3时频繁触发Segmentation Fault (core dumped)错误。代码编译无异常,命令行参数数量(共6个)也正确,但自行调试无法定位根源,求协助排查。

原代码

#ifndef MY_GAME_H
#define MY_GAME_H
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <curses.h>

#define EMPTY ' '
#define BORDER '*'
#define PLAYER 'P'
#define GOAL 'G'
#define COLLAPSED 'X'

typedef struct position {
    int row;
    int col;
} Position;

typedef struct node {
    Position pos;
    char collapsed_color;
    struct node* next;
} Node;

typedef struct list {
    Node* head;
    int size;
} List;

int ROW, COL;
char** map;
Position player, goal;
List history;

void initialize() {
    int i, j;

    map = (char**)malloc(ROW * sizeof(char*));
    for (i = 0; i < ROW; i++) {
        map[i] = (char*)malloc(COL * sizeof(char));
        for (j = 0; j < COL; j++) {
            if (i == 0 || i == ROW - 1 || j == 0 || j == COL - 1) {
                map[i][j] = BORDER;
            }
            else {
                map[i][j] = EMPTY;
            }
        }
    }

    if (history.size > 0) {
        player = history.head->pos;
        goal = history.head->next->pos;
    } else {
        // initialize player and goal to default values
        player.row = 1;
        player.col = 1;
        goal.row = ROW - 2;
        goal.col = COL - 2;
    }

    map[player.row][player.col] = PLAYER;
    map[goal.row][goal.col] = GOAL;
}


void print_map() {
    int i, j;
    for (i = 0; i < ROW; i++) {
        for (j = 0; j < COL; j++) {
            if (i == player.row && j == player.col) {
                attron(COLOR_PAIR(1));
                printw("%c ", map[i][j]);
                attroff(COLOR_PAIR(1));
            }
            else if (i == goal.row && j == goal.col) {
                attron(COLOR_PAIR(2));
                printw("%c ", map[i][j]);
                attroff(COLOR_PAIR(2));
            }
            else if (map[i][j] == COLLAPSED) {
                if (i == history.head->pos.row && j == history.head->pos.col) {
                    attron(COLOR_PAIR(3));
                    printw("%c ", map[i][j]);
                    attroff(COLOR_PAIR(3));
                }
                else {
                    printw("%c ", map[i][j]);
                }
            }
            else {
                printw("%c ", map[i][j]);
            }
        }
        printw("\n");
    }
}


void collapse_floor() {
    int i, j;
    Node* new_node = (Node*)malloc(sizeof(Node));
    new_node->collapsed_color = 'r';
    do {
        new_node->pos.row = rand() % (ROW - 2) + 1;
        new_node->pos.col = rand() % (COL - 2) + 1;
    } while (map[new_node->pos.row][new_node->pos.col] != EMPTY);
    new_node->next = history.head;
    history.head = new_node;
    history.size++;

    map[new_node->pos.row][new_node->pos.col] = COLLAPSED;
}    

void move_player(int row_offset, int col_offset) {
    if (map[player.row + row_offset][player.col + col_offset] == EMPTY) {
        map[player.row][player.col] = EMPTY;
        player.row += row_offset;
        player.col += col_offset;
        map[player.row][player.col] = PLAYER;

        collapse_floor();
    }
    else if (map[player.row + row_offset][player.col + col_offset] == GOAL){
        map[player.row][player.col] = EMPTY;
        player.row += row_offset;
        player.col += col_offset;
        map[player.row][player.col] = PLAYER;
        print_map();
        printf("\nYou Win!\n");
        exit(0);
    }
}

void undo_move() {
    if (history.size > 1) {
        Node* old_node = history.head;
        history.head = history.head->next;
        history.size--;

        if (map[player.row][player.col] == COLLAPSED) {
            map[player.row][player.col] = EMPTY;
        }

        player = history.head->pos;

        if (map[player.row][player.col] == COLLAPSED) {
            map[player.row][player.col] = PLAYER;
            map[old_node->pos.row][old_node->pos.col] = COLLAPSED;
        } else {
            map[player.row][player.col] = PLAYER;
            if (map[old_node->pos.row][old_node->pos.col] == COLLAPSED) {
                map[old_node->pos.row][old_node->pos.col] = EMPTY;
            }  
             map[old_node->pos.row][old_node->pos.col] = COLLAPSED;
        }

        free(old_node);
    }    
}

void end_game() {
    printf("Game over!\n");
    exit(0);  // Terminate the program with exit status 0 (success)
}

int main(int argc, char* argv[]) {
    int i;
    srand(time(NULL));

    if (argc != 7) {
        printf("Usage: ./game ROW COL player_row player_col goal_row goal_col\n");
        exit(1);
    }

    ROW = atoi(argv[1]);
    COL = atoi(argv[2]);
    player.row = atoi(argv[3]);
    player.col = atoi(argv[4]);
    goal.row = atoi(argv[5]);
    goal.col = atoi(argv[6]);

    if (player.row < 0 || player.row >= ROW || player.col < 0 || player.col >= COL ||
        goal.row < 0 || goal.row >= ROW || goal.col < 0 || goal.col >= COL) {
        printf("Invalid player or goal position.\n");
        exit(1);
    }

    history.head = (Node*)malloc(sizeof(Node));
    history.head->pos = player;
    history.head->collapsed_color = 'r';
    history.head->next = NULL;
    history.size = 1;

    initscr();
    start_color();
    init_pair(1, COLOR_BLUE, COLOR_BLACK);
    init_pair(2, COLOR_YELLOW, COLOR_BLACK);
    init_pair(3, COLOR_RED, COLOR_BLACK);

    initialize();

    print_map();

    while (1) {
        int ch = getch();
        switch (ch) {
            case 'w':
                move_player(-1, 0);
                break;
            case 'a':
                move_player(0, -1);
                break;
            case 's':
                move_player(1, 0);
                break;
            case 'd':
                move_player(0, 1);
                break;
            case 'u':
                undo_move();
                break;
            case 'q':
                end_game();
                break;
        }

        if (player.row == goal.row && player.col == goal.col) {
        end_game();
        }

        print_map();
    }

    endwin();

    for (i = 0; i < ROW; i++) {
        free(map[i]);
    }

    free(map);

    while (history.size > 0) {
        undo_move();
    }

    return 0;
}

#endif /* MY_GAME_H */

核心错误根源

  • 空指针解引用(直接触发段错误):
    initialize函数中,history.size > 0分支直接访问history.head->next->pos,但main初始化history时仅创建了一个节点,next为NULL,导致空指针解引用。
  • print_map的空指针风险:
    多次执行undo_move后,history.head可能变为NULL,此时访问history.head->pos会触发段错误。
  • map越界访问:
    move_player未检查目标位置是否为边界,玩家向边界移动时会访问map的非法索引。
  • Curses与标准输出冲突:
    end_game和胜利逻辑使用printf输出,Curses初始化后应使用printw,printf会破坏Curses的屏幕缓冲区,引发异常。

关键修复代码

  1. 修正initialize的指针访问:

    void initialize() {
        int i, j;
    
        map = (char**)malloc(ROW * sizeof(char*));
        for (i = 0; i < ROW; i++) {
            map[i] = (char*)malloc(COL * sizeof(char));
            for (j = 0; j < COL; j++) {
                if (i == 0 || i == ROW - 1 || j == 0 || j == COL - 1) {
                    map[i][j] = BORDER;
                }
                else {
                    map[i][j] = EMPTY;
                }
            }
        }
    
        // 直接使用main中初始化的player和goal,避免空指针访问
        map[player.row][player.col] = PLAYER;
        map[goal.row][goal.col] = GOAL;
    }
    
  2. print_map添加空指针判断:

    else if (map[i][j] == COLLAPSED) {
        if (history.head != NULL && i == history.head->pos.row && j == history.head->pos.col) {
            attron(COLOR_PAIR(3));
            printw("%c ", map[i][j]);
            attroff(COLOR_PAIR(3));
        }
        else {
            printw("%c ", map[i][j]);
        }
    }
    
  3. move_player添加边界检查:

    void move_player(int row_offset, int col_offset) {
        int new_row = player.row + row_offset;
        int new_col = player.col + col_offset;
        // 检查是否越界或碰到边界
        if (new_row <= 0 || new_row >= ROW-1 || new_col <=0 || new_col >= COL-1) {
            return;
        }
        if (map[new_row][new_col] == EMPTY) {
            map[player.row][player.col] = EMPTY;
            player.row = new_row;
            player.col = new_col;
            map[player.row][player.col] = PLAYER;
    
            collapse_floor();
        }
        else if (map[new_row][new_col] == GOAL){
            map[player.row][player.col] = EMPTY;
            player.row = new_row;
            player.col = new_col;
            map[player.row][player.col] = PLAYER;
            print_map();
            printw("\nYou Win!\n");
            refresh();
            endwin();
            exit(0);
        }
    }
    
  4. 修正end_game输出方式:

    void end_game() {
        printw("Game over!\n");
        refresh();
        endwin();
        exit(0);
    }
    

内容的提问来源于stack exchange,提问作者Necro007

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 15:27:01