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

C程序中如何动态分配与重分配char数组实现卡牌游戏玩家手牌存储

问题核心原因
  • 全局变量被局部同名变量遮蔽:你在initializePlayers函数内重新定义了同名的player1Hand、player2Hand局部变量,所有内存分配操作都作用在局部变量上,全局的手牌数组始终为NULL,导致后续判断逻辑失效。
  • 发牌函数内重复定义局部变量:dealCards里你又定义了局部的char *player1Hand,不仅和全局的char**类型不匹配,realloc操作的是未初始化的局部栈变量,直接触发内存错误。
  • 内存分配逻辑错误:
    1. 你要存的是char*类型的手牌数组,分配大小应该按「手牌数量 * 指针大小」计算,不是按char大小计算
    2. realloc直接赋值给原指针会导致内存泄漏:如果realloc失败返回NULL,原内存地址会被覆盖无法释放
    3. 发牌前先校验剩余牌数再分配内存,你当前先分配再校验的逻辑完全颠倒
  • 发牌索引逻辑错误:两个玩家共用同一个循环变量i作为手牌索引,会导致玩家2的手牌存储位置错位,数据写入越界。
修复方案
  1. 首先补充必要的全局变量,初始值设为NULL,不要在函数内重新定义同名变量遮蔽全局变量:
// 全局变量定义,初始化为NULL,方便后续分配判断
char **player1Hand = NULL;
char **player2Hand = NULL;
int player1CardCnt = 0; // 记录玩家1当前手牌数
int player2CardCnt = 0; // 记录玩家2当前手牌数
extern int cardsRemaining; // 牌堆剩余牌数,根据你实际定义调整
extern int placeInDeck; // 牌堆当前发牌位置,根据你实际定义调整
  1. 删掉冗余的初始化函数,直接在发牌函数里判断指针是否为NULL完成首次分配,realloc本身支持传入NULL,效果和malloc完全一致,不需要单独写初始化逻辑。
  2. 修复发牌函数的内存分配和发牌逻辑:
int dealCards(char *(*deck)[13], int numCardsToDeal)
{
    // 先校验剩余牌数,调整实际发牌量
    int actualDeal = numCardsToDeal;
    if (actualDeal > cardsRemaining)
    {
        printf("Number of cards to deal exceeds remaining deck. Remaining deck will be dealt.\n");
        actualDeal = cardsRemaining;
    }
    if (actualDeal <= 0) return 0;

    // 计算两个玩家本次新增的手牌数
    int addP1 = (actualDeal + 1) / 2;
    int addP2 = actualDeal / 2;

    // 重分配玩家1手牌数组,用临时指针接返回值避免内存泄漏
    char **tmp = realloc(player1Hand, (player1CardCnt + addP1) * sizeof(char*));
    if (tmp == NULL) {
        perror("realloc player1 hand failed");
        return 0;
    }
    player1Hand = tmp;

    // 重分配玩家2手牌数组
    tmp = realloc(player2Hand, (player2CardCnt + addP2) * sizeof(char*));
    if (tmp == NULL) {
        perror("realloc player2 hand failed");
        return 0;
    }
    player2Hand = tmp;

    // 发牌逻辑
    int deckIdx = placeInDeck;
    int p1Idx = player1CardCnt;
    int p2Idx = player2CardCnt;
    for (int i = 0; i < actualDeal; i++)
    {
        char *card = deck[deckIdx / 13][deckIdx % 13];
        if (i % 2 == 0) {
            // 给玩家1发牌,如果需要单独存储牌面内容可以用strdup(card)
            player1Hand[p1Idx++] = card;
            printf("玩家1拿到:%c\n", *card);
        } else {
            // 给玩家2发牌
            player2Hand[p2Idx++] = card;
            printf("玩家2拿到:%c\n", *card);
        }
        deckIdx++;
        cardsRemaining--;
    }
    placeInDeck = deckIdx;
    player1CardCnt = p1Idx;
    player2CardCnt = p2Idx;

    return actualDeal;
}

附加注意点

如果你的牌面内容是需要单独存储的,不是直接引用牌堆里的字符串,可以把赋值部分换成player1Hand[p1Idx++] = strdup(card);,游戏结束后记得释放内存避免泄漏:

void freeHands(void) {
    for (int i = 0; i < player1CardCnt; i++) {
        free(player1Hand[i]); // 直接引用牌堆内存的不需要这步
    }
    free(player1Hand);
    player1Hand = NULL;
    player1CardCnt = 0;

    for (int i = 0; i < player2CardCnt; i++) {
        free(player2Hand[i]); // 直接引用牌堆内存的不需要这步
    }
    free(player2Hand);
    player2Hand = NULL;
    player2CardCnt = 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 21:45:02