我写了一个二叉搜索树来存储一些排序的单词。通常这种做法,我是通过为每个新词进入时为二叉树分配新的内存块来实现的。但是,奇怪的是,我只能为二进制搜索树分配新内存两次 ,这意味着在第一次和第二次时,一切都很好,但程序在第三次内存分配时崩溃。
这是我的代码:
inputWord.c
/* I pass in the firstNode, and the word I wanna store, and its quantity as argument*/
int inputWord(BSTnode* Node,char* word,int num){
BSTnode* ptr=Node; //ptr was defined to track the location of the node.
while(1){
if(stricmp(word,ptr->data)>0){
/*If the current node already have a rightchild then ptr move to it, and do comparison again*/
if(ptr->rightchild!=NULL){
ptr=ptr->rightchild;
printf("Moving to another (right) node now!!\n");
continue;
}
/*If the current node have no rightchild, then make a new one for it and store the word and its quantity*/
else{
ptr->rightchild=malloc(sizeof(BSTnode));
if(!(ptr->rightchild))
return 1;
ptr=ptr->rightchild;
ptr->rightchild=NULL;
ptr->leftchild=NULL;
strcpy(ptr->data,word);
ptr->num=num;
break;
}
}
else if(stricmp(word,ptr->data)<0){
/*it's all the same as the rightchild part*/
if(ptr->leftchild!=NULL){
ptr=ptr->leftchild;
continue;
}
else{
ptr->leftchild=malloc(sizeof(BSTnode));
if(!(ptr->leftchild))
return 1;
ptr=ptr->leftchild;
ptr->leftchild=NULL;
ptr->rightchild=NULL;
strcpy(ptr->data,word);
ptr->num=num;
break;
}
}
/*If the word have already been stored in the tree, print out this message*/
else{
fprintf(stdout,"It is exactly the same word!!\n");
return 0;
}
}
return 0;
}
我在上面做了一些必要的评论,以帮助你理解我的意图。希望这会有所帮助。
如您所见,该功能非常简单明了。它确实适用于前两次调用。但是当调用第三次时间!!(总是第三次)时它会崩溃。
所以我做了一些测试。而现在我很确定它在线路上崩溃了
ptr->leftchild=malloc(sizeof(BSTnode));
(明确指出firstNode
的数据已初始化为""
进行比较。我传入了单词&#34; The
&#34;首先和&# 34; Project
&#34;秒和&#34; Gutenberg
&#34;第三。BSTnode
的结构是
typedef struct BSTnode{
char data[20];
struct BSTnode* leftchild;
struct BSTnode* rightchild;
int num;
}BSTnode;
)
我如何进行该测试如下所示。 (它是相同的代码,只有一些额外的print
语句用于测试)
int inputWord(BSTnode* Node,char* word,int num){
printf("Enter inputWord() successfully!!\n");
BSTnode* ptr=Node;
while(1){
if(stricmp(word,ptr->data)>0){
if(ptr->rightchild!=NULL){
ptr=ptr->rightchild;
printf("Moving to another (right) node now!!\n");
continue;
}
else{
printf("I need a new rightchild!!\n");
ptr->rightchild=malloc(sizeof(BSTnode));
printf("New rightchild created successfully!!\n");
if(!(ptr->rightchild))
return 1;
ptr=ptr->rightchild;
ptr->rightchild=NULL;
ptr->leftchild=NULL;
printf("......In line 27 now!!\n");
strcpy(ptr->data,word);
printf("Copied successfully!!!..In line 29 now!!\n");
ptr->num=num;
fprintf(stdout,"New data '%s' successfully inserted into a new (right) node at %p (value of pointer)\n",word,ptr);
break;
}
}
else if(stricmp(word,ptr->data)<0){
if(ptr->leftchild!=NULL){
ptr=ptr->leftchild;
printf("Moving to another (left) node now!!\n");
continue;
}
else{
printf("I need a new left child!!!\n");
ptr->leftchild=malloc(sizeof(BSTnode));
printf("New leftchild created successfully!!\n");
if(!(ptr->leftchild))
return 1;
ptr=ptr->leftchild;
ptr->leftchild=NULL;
ptr->rightchild=NULL;
printf("......In line 47 now!!\n");
strcpy(ptr->data,word);
printf("Copied successfully!!!..In line 51 now!!\n");
ptr->num=num;
fprintf(stdout,"New data '%s' successfully inserted into a new (left) node at %p (value of pointer)\n",word,ptr);
break;
}
}
else{
fprintf(stdout,"Nothing else to insert!!\n");
return 0;
}
}
return 0;
}
正如您所看到的,通过一些print
语句告诉我我在哪里,我可以确定程序崩溃的位置。
知道为什么它总是在第三次崩溃?
################################################## ##################### 3的main.c
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<stdbool.h>
#include "wordCount.h"
void prompt(BSTnode*,FILE*);
char arr[20]={0};
int main()
{
BSTnode* firstNode=malloc(sizeof(BSTnode));
firstNode->leftchild=NULL;
firstNode->rightchild=NULL;
strcpy(firstNode->data,"");
firstNode->num=0;
FILE* fs=fopen("testfile.txt","r");
if(!fs){
printf("Failed to open fiel!!\n");
return 2;
}
while(1){
if(ferror(fs))
perror("there is a error in fs in the beginning of while loop!\n");
prompt(firstNode,fs);
}
return 0;
}
void prompt(BSTnode* Node,FILE* fs){
int i=0;
printf("Please select\n1.find and input a word into the binary tree\n2.print only one data\n3.Exit\n");
if(scanf("%d",&i)!=1){
printf("scanf failed!!\nplease input a valid number!!\n");
//fflush(stdin);
return;
}
getchar();
switch(i){
case 1:
{
memset(arr,'\0',20); //since the "arr" is used to hold the newWord founded and returned, it should be clear first every time
char* newWord=findWord(fs);
int totalNumberOfTheWord=wordCount(fs,newWord);
inputWord(Node,newWord,totalNumberOfTheWord);
break;
}
case 2:
printOneNode(Node);
break;
case 3:
exit(0);
default:
printf("Please input a valid number!(1-3)");
}
}
此外, wordCount.h :
#ifndef WORDCOUNT_H
#define WORDCOUNT_H
#include<stdlib.h>
#include<stdio.h>
typedef struct BSTnode{
char data[20];
struct BSTnode* leftchild; //if less than, put it on the left
struct BSTnode* rightchild; //if greater than, on the right
int num;
}BSTnode;
int inputWord(BSTnode*,char*,int);
char* findWord(FILE*);
int wordCount(FILE*,char*);
int printOneNode(BSTnode*);
#endif
函数prompt()
用于提示用户决定是否继续进行单词搜索。
完整源代码:
wordCount.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "wordCount.h"
int wordCount(FILE* fs,char* word)
{
int num=0;
rewind(fs);
size_t n1=sizeof(word);
size_t n2=strlen(word);
char* buff=malloc(n1) ;
if(buff==NULL)
return 1;
memset(buff,'\0',n1);
/* I count the word by moving byte by byte and do comparison*/
if (fs != NULL) {
if (n2 == fread(buff, 1,n2, fs)) {
do {
if (strnicmp(buff,word,n2) == 0)
num++;
memmove(buff, buff+1,n2-1);
} while (1 == fread(buff+n2-1, 1, 1, fs));
// I think I might optimize
// this using KMP algorithm
}
}
free(buff);
return num;
}
findWord.c
#include<string.h>
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include "wordCount.h"
extern char arr[20];
char* findWord(FILE* fs)
{
static long pos=0;
fseek(fs,pos,SEEK_SET);
if(ferror(fs)){
perror("fseek() failed!!!\n");
fprintf(stderr,"fseek() failed in file %s\n",__FILE__);
exit(EXIT_FAILURE);
}
char chr[1]={0};
bool flag1=false;
bool flag2=false;
while((1==fread(chr,1,1,fs))&&(!(flag1==false&&flag2==true))){
// This would make the findword() function
// find only a single word once
if(chr[0]!=32){
strncat(arr,chr,1);
flag2=true;
flag1=true;
}
else
flag1=false;
}
/*the key method that I use to find a new word is that I use two 'bool' flags: flag1 and flag2.
*Only when the "arr" is filled only with character, not a single space, will the flag1 be false and flag2 be true, thus breaking the while loop*/
pos=ftell(fs)-1;
//maybe everytime you use "fseek()", "ftell()", the
//file-position will move one byte ahead.
return arr;
}
printOneNode.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"wordCount.h"
int printOneNode(BSTnode* Node){
BSTnode* ptr=Node;
while(1){
printf("Select which side of node do you want to print now(l/r)?(q for quit) ");
char a;
getchar(); //this is used to consume the newline character left
//fflush(stdin);
if(scanf("%c",&a)!=1){
printf("scanf failed!!");
return 1;
}
switch(a){
case 'l':
{
if(ptr->leftchild!=NULL){
ptr=ptr->leftchild;
printf("\t%s\n",ptr->data);
}
else
printf("There is no more leftchild\n");
break;
}
case 'r':
{
if(ptr->rightchild!=NULL){
ptr=ptr->rightchild;
printf("\t%s\n",ptr->data);
}
else
printf("There is no more rightchild!\n");
break;
}
case 'q':
return 0;
default:
return 0;
}
}
}
函数findWord()
用于查找要插入的新单词。例如,如果This is a lovely place...
中有字符textfile.txt
,则findWord()
首先会找到单词This
,然后找is
,然后a
1}}第三,等等(这就是我将pos
定义为静态变量以跟踪位置的原因。)
函数wordCount()
用于计算findWord()
中testfile.txt
返回的单词的显示时间。
函数printOneNode()
用于根据用户的意愿打印出单个节点的数据。我设计了这个函数但还没有使用它,这意味着在prompt()
函数中我总是选择&#34;在二叉搜索树中找到并输入一个新单词&#34;)。因此,这可能不是导致我的程序崩溃的原因,偶尔会出现#34;。
总结一下,我的常规是:
testfile.txt
findWord()
中找到新单词
wordCount()
inputWord()
重复一遍。
我不能让这个程序变得更小以使其更容易理解,因为它必须找到一个单词并计算它插入它。但是你可以在某种程度上忽略printOneNode()
函数。
至于testfile.txt
,我在评论区发布了以下链接。感谢
答案 0 :(得分:4)
编辑:这是我之前发布的帖子(见下文)的修正案,详细说明了此代码中发现的更严重的问题。
在wordCount
中有缓冲区溢出。缓冲区溢出是UB。
n1
分配buff
个字节指向。偶然的机会,你碰巧知道多少个字节?也许您应该检查,然后自己回答:您可以在该对象中存储多少字节?n2
字节读入buff
。哪个更高,n1
或n2
?你看过那个吗?如果你试图将24个鸡蛋放入一个只装有12个鸡蛋的纸盒中,会发生什么?我认为这里的问题是你不了解sizeof
运营商;它不是函数......相反,它是一个非常类似于&address-of
和-negation
运算符的运算符,除了 sizeof
对类型的运算(或用表达式表示;它会计算出该类型对象的大小。
为了澄清,在以下代码片段中,n1
为sizeof (char *)
,这可能与您的意图不同。
int wordCount(FILE* fs,char* word)
{
int num=0;
rewind(fs);
size_t n1=sizeof(word);
size_t n2=strlen(word);
char* buff=malloc(n1);
inputWord
似乎在word
指向某个字符串的情况下运行,但该值似乎来自您的程序中的findWord
,这并不是必需的string(因为它使用strncat
)。 更多未定义的行为!这真是令人惊讶吗?
上一个回答:
首先,这段代码甚至无法编译。您在inputWord(Node,newWord,totalNumberOfTheWord)
内prompt
之后立即错过了分号。也许您还没有注意到这些错误,并且您运行的是我们没有源代码的过时二进制文件?
其次,即使要编译此代码,也有许多undefined behaviour的实例,例如:
malloc
返回NULL
并且您尝试修改NULL
指向BSTnode* firstNode=malloc(sizeof(BSTnode));
紧随其后firstNode->leftchild=NULL;
。也许您可以这样声明firstNode
:BSTnode firstNode = { 0 };
并使用&firstNode
创建指向它的指针...毕竟,你真的应该选择最合适的存储持续时间而不是默认每次都要malloc
。在这方面,我强烈建议将分配逻辑与数据结构逻辑分开;如果您需要进一步详细说明,请考虑scanf
的设计方式。fflush(stdin);
。每当您第一次使用某个功能时,您应该始终非常仔细地阅读和理解本手册 ......而且这不仅仅是为了提供有关如何设计功能的见解。如果您在使用fflush
之前已经阅读并完全理解 this fflush
manual,那么您将永远不会使用这个有问题的代码。考虑使用scanf("%*[^\n]"); getchar();
之类的东西。%p
格式指令,该指令要求void *
指针作为相应的参数。但是,您提供的相应参数的类型为struct BSTnode *
。根据{{3}},&#34;如果任何参数不是相应转换规范的正确类型,则行为未定义。&#34; 即使您没有修复这些未定义的行为,当您提供虚拟函数代替findWord
和wordCount
时,此代码也可能巧妙地在您的系统上运行。但是,它不需要在所有系统上以相同的方式工作,这对您来说意味着崩溃可能发生在我们没有的地方。解决这些问题。
这些问题表明您的findWord
和wordCount
功能也不一定值得信赖和万无一失;他们可能会在一个环境中为你工作而在另一个环境中失败,或者更糟糕的是,也许他们也过时了!您应该通过在其位置提供虚拟功能来验证问题是您认为的问题。毕竟,这是the fprintf
manual。
不,我对这个问题没有兴趣,因为它质量极差;正如我之前提到的,这个问题依赖于语法上错误的代码编译正确,所以我们无法重现你看到的结果。即使我们修复了语法错误,我们也必须填写空白(这是您的工作),这会将不确定性方面引入任何可能的答案。关于这个问题我唯一感兴趣的就是让关闭的过程。