C程序中如何动态分配与重分配char数组实现卡牌游戏玩家手牌存储
问题核心原因
- 全局变量被局部同名变量遮蔽:你在
initializePlayers函数内重新定义了同名的player1Hand、player2Hand局部变量,所有内存分配操作都作用在局部变量上,全局的手牌数组始终为NULL,导致后续判断逻辑失效。 - 发牌函数内重复定义局部变量:
dealCards里你又定义了局部的char *player1Hand,不仅和全局的char**类型不匹配,realloc操作的是未初始化的局部栈变量,直接触发内存错误。 - 内存分配逻辑错误:
- 你要存的是
char*类型的手牌数组,分配大小应该按「手牌数量 * 指针大小」计算,不是按char大小计算 realloc直接赋值给原指针会导致内存泄漏:如果realloc失败返回NULL,原内存地址会被覆盖无法释放- 发牌前先校验剩余牌数再分配内存,你当前先分配再校验的逻辑完全颠倒
- 你要存的是
- 发牌索引逻辑错误:两个玩家共用同一个循环变量
i作为手牌索引,会导致玩家2的手牌存储位置错位,数据写入越界。
修复方案
- 首先补充必要的全局变量,初始值设为
NULL,不要在函数内重新定义同名变量遮蔽全局变量:
// 全局变量定义,初始化为NULL,方便后续分配判断 char **player1Hand = NULL; char **player2Hand = NULL; int player1CardCnt = 0; // 记录玩家1当前手牌数 int player2CardCnt = 0; // 记录玩家2当前手牌数 extern int cardsRemaining; // 牌堆剩余牌数,根据你实际定义调整 extern int placeInDeck; // 牌堆当前发牌位置,根据你实际定义调整
- 删掉冗余的初始化函数,直接在发牌函数里判断指针是否为
NULL完成首次分配,realloc本身支持传入NULL,效果和malloc完全一致,不需要单独写初始化逻辑。 - 修复发牌函数的内存分配和发牌逻辑:
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
相关产品推荐
相关产品推荐

