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

文件指针声明干扰无关函数局部变量的原因排查

类拼字游戏局部数组乱码问题分析与解决

问题背景

我开发的类拼字游戏作业程序,核心通过getLetterDistribution函数统计单词字母出现次数到长度为26的int数组,用来对比玩家手牌字母数量。原本运行正常,但在init函数中添加FILE* dictionary = fopen("dictionary.txt","r");的文件指针声明后,gameLoop里的局部数组testHandDist输出乱码,全局数组guessDist却正常。

相关代码如下:

核心函数定义

//Define
void getLetterDistribution(char *word, int *dist){
 for(int i=0;i<26;i++){
  printf("%c",i+65);
 }
 printf("\n");
 for(int i=0;i<26;i++){
  int j = 0;
  do{
   if(word[j] == i+65){ //ASCII大写字母从65开始
    dist[i]++; //对应字母计数+1
   }
   j++;
  }while(word[j] != '\0'); //遍历到单词结束
  printf("%d",dist[i]);
 }
 printf("%ls\n",dist);
}

调用逻辑

//Call
void gameLoop(){
 char testHand[12] = "ACEROLAORIO\0"; //测试用手牌
 int testHandDist[26];
 do{
  displayWorld();
  printf("Hand: %s\n",testHand);
  acceptInput();
  getLetterDistribution(testHand,handDist);
  getLetterDistribution(buffer,guessDist);
  printf("%d\n",compareCounts(guessDist,handDist));//测试输出对比结果
 }while(isDone()!=1);
}

修改前后的init函数

修改前(正常运行):

int init(){
 srand(time(NULL));
 return 0;
}

修改后(出现乱码):

int init(){
 srand(time(NULL));
 FILE* dictionary = fopen("dictionary.txt","r");
 return 0; //占位代码
}

输出对比

修改前(正常):

--------------------------
Hand: ACEROLAORIO
Enter a Guess: core
CORE
ABCDEFGHIJKLMNOPQRSTUVWXYZ
20101000100100300200000000
ABCDEFGHIJKLMNOPQRSTUVWXYZ
00101000000000100100000000
1

修改后(局部数组乱码):

--------------------------
Hand: ACEROLAORIO
Enter a Guess: core
CORE
ABCDEFGHIJKLMNOPQRSTUVWXYZ
-19329427032558-195148716325586984704112195269939371221952698470411219526993937122195369847041021952-195303658325580200405811584327644058118803276469846781321952
ABCDEFGHIJKLMNOPQRSTUVWXYZ
00101000000000100100000000
0

问题原因

  1. 局部数组未初始化:testHandDist是栈上的局部数组,C语言中局部变量默认不会自动初始化,内存里是之前栈遗留的垃圾值。之前init函数里变量少,栈空间刚好残留的是0值,所以统计结果正常;添加FILE* dictionary后,栈的布局被改变,testHandDist所在的栈区域残留的是随机垃圾值,dist[i]++相当于在随机数基础上累加,自然出现乱码。
  2. 全局数组的特殊性:guessDist是全局数组,全局变量会被自动初始化为0,所以不管栈怎么变,它的初始值都是0,统计结果正常。
  3. 函数设计缺陷:getLetterDistribution函数没有对传入的dist数组做清零操作,完全依赖调用方保证数组初始值为0,一旦调用方没初始化(比如局部数组),就会出问题。

修复方案

方案1:显式初始化局部数组

在声明testHandDist时直接初始化所有元素为0:

void gameLoop(){
 char testHand[12] = "ACEROLAORIO\0";
 int testHandDist[26] = {0}; //显式初始化所有元素为0
 //...后续逻辑不变
}

方案2:在统计函数内先清零数组

修改getLetterDistribution,开头先把dist数组的26个元素全部设为0,不管调用方有没有初始化:

void getLetterDistribution(char *word, int *dist){
 //先清零数组
 for(int i=0;i<26;i++){
  dist[i] = 0;
 }
 //...后续打印字母、统计逻辑不变
}

额外修复:关闭文件指针

init函数里打开了文件但没关闭,会造成资源泄漏,要加上关闭操作:

int init(){
 srand(time(NULL));
 FILE* dictionary = fopen("dictionary.txt","r");
 //后续如果有字典读取逻辑,放在这里
 if(dictionary != NULL){ //要判断文件是否成功打开
  fclose(dictionary); //用完及时关闭
 }
 return 0;
}

避免同类错误的规范

  • 局部变量必须显式初始化:栈上的局部数组、变量默认是垃圾值,使用前一定要手动初始化(比如int arr[26] = {0};)。
  • 函数参数要做防御性处理:如果函数依赖参数的初始状态(比如统计用的数组需要从0开始),就在函数内部主动初始化,不要依赖调用方的操作。
  • 文件资源及时释放:用fopen打开文件后,必须用fclose关闭,避免文件描述符泄漏;还要判断fopen的返回值是否为NULL,防止文件打开失败的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:53:14