文件指针声明干扰无关函数局部变量的原因排查
类拼字游戏局部数组乱码问题分析与解决
问题背景
我开发的类拼字游戏作业程序,核心通过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
问题原因
- 局部数组未初始化:
testHandDist是栈上的局部数组,C语言中局部变量默认不会自动初始化,内存里是之前栈遗留的垃圾值。之前init函数里变量少,栈空间刚好残留的是0值,所以统计结果正常;添加FILE* dictionary后,栈的布局被改变,testHandDist所在的栈区域残留的是随机垃圾值,dist[i]++相当于在随机数基础上累加,自然出现乱码。 - 全局数组的特殊性:
guessDist是全局数组,全局变量会被自动初始化为0,所以不管栈怎么变,它的初始值都是0,统计结果正常。 - 函数设计缺陷:
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
相关产品推荐
相关产品推荐

