我的程序只能使用 malloc() 分配内存几次

My program can only use malloc() to allocate memory a few times

我写了一个二叉搜索树来存储一些排序的词。通常的做法是,每次出现新词时,我都会为二叉树分配新的内存块。但是,奇怪的是,我只能为二叉搜索树分配新内存 两次,这意味着在第一次第二次时间一切正常,但程序在第三次内存分配时崩溃。

这是我的代码:

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;
}

我在上面做了一些必要的评论,以帮助您理解我的 intention.Hopefully 这会有所帮助。

如您所见,该函数非常直接和简单。它在 前两次调用 时确实有效。但是在调用 第三次 时它崩溃了!!(总是第三次)。

所以我做了一些测试。现在我很确定它会在

行崩溃

ptr->leftchild=malloc(sizeof(BSTnode));

(说明一下firstNode的数据是用""初始化的,用于对比,我先传入了“The”和“Project” " 第二个和 "Gutenberg" 第三个。 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,'[=13=]',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()用于提示用户决定是否继续查词

################################################## ###################3

完整源代码:

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,'[=15=]',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()用于查找要插入的新词。例如,如果textfile.txt中有字符串This is a lovely place...,那么findWord()会先找出一个词This,然后是is,然后是a 第三,等等(这就是我将 pos 定义为静态变量以跟踪位置的原因。)

函数wordCount()用于计算findWord()返回的单词在testfile.txt.

中出现了多少次

函数printOneNode()用于根据用户的意愿打印出单个节点的数据。我设计了这个功能,但还没有使用它,这意味着在 prompt() 功能中我总是选择 "find and input a new word into the binary search tree")。所以这可能不是导致我的程序崩溃的原因 "occasionally".

总而言之,我的例程是:

  1. 提示用户询问是否查找并插入新词(总是是)
  2. 使用 findWord()
  3. testfile.txt 中找到一个新词
  4. 使用wordCount()
  5. 计算数量
  6. 使用inputWord()
  7. 将其插入到二叉搜索树中

重复一遍。

我不能再让这个程序更小以使其更易于理解,因为它必须找到一个词并计算它并插入它。但是你可以在某种程度上忽略那个 printOneNode() 函数。

至于testfile.txt,我已经在评论区下面发了link。谢谢

编辑:这是对我之前post(见下文)的修正,详细说明了这段代码中发现的更严重的问题。

wordCount中有缓冲区溢出。缓冲区溢出是UB。

  • 您正在分配 n1 个字节供 buff 指向。碰巧,你碰巧知道那是多少字节?也许您应该检查一下,然后自己回答这个问题:您可以在该对象中存储多少字节?
  • 然后您将尝试将 n2 个字节读入 buffn1n2 哪个更大?你看过那个吗?如果您尝试将 24 个鸡蛋放入一个只能容纳 12 个的纸箱中,会发生什么情况?

我认为这里的问题是你不理解sizeof运算符;它不是一个函数...相反,它是一个非常类似于 &address-of-negation 运算符的运算符,除了 sizeof 对 (或表示为)表达式;它评估该类型对象的大小。

澄清一下,在下面的代码片段中,n1sizeof (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 ,这不一定产生字符串(因为它使用 strncat)。 更多 未定义的行为!是不是很意外?


上一个回答:

首先,这段代码甚至无法编译。 prompt 中的 inputWord(Node,newWord,totalNumberOfTheWord) 后面缺少一个分号。也许您没有注意到这些错误,您是 运行 一个我们没有源代码的过时二进制文件?

其次,即使这段代码可以编译,也有许多 undefined behaviour 的实例,例如:

  • malloc returns NULL 并且您试图修改 NULL 指向 的对象时,会发生空指针取消引用因此。例如BSTnode* firstNode=malloc(sizeof(BSTnode)); 紧接着是 firstNode->leftchild=NULL;。也许您可以像这样声明 firstNodeBSTnode firstNode = { 0 }; 并使用 &firstNode 创建指向它的指针...毕竟,您确实应该选择最合适的存储期限而不是而不是每次都默认 malloc。关于这一点,我强烈建议将分配逻辑与数据结构逻辑分开;如果您需要进一步阐述,请考虑 scanf 是如何设计的。
  • fflush(stdin);. Whenever you use a function for the first time, you should always read and understand the manual very carefully... and that's not just to provide insight on how you should be designing your functions. If you had read and fully understood this fflush manual 在使用 fflush 之前,您永远不会使用这个有问题的代码。考虑在其位置使用 scanf("%*[^\n]"); getchar(); 之类的东西。
  • 在一些地方,您使用了 %p 格式指令,它需要一个 void * 指针作为相应的参数。但是,您提供的相应参数的类型为 struct BSTnode *。根据the fprintf manual、"If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined."

即使您不修复这些未定义的行为,当您提供虚拟函数代替 findWordwordCount 时,此代码也可能巧合地在您的系统上运行。但是,它不需要在所有系统上都以相同的方式工作,这意味着对您来说,崩溃可能发生在我们不会发生的地方。解决这些问题。

这些问题表明您的 findWordwordCount 函数也不一定值得信赖和万无一失;它们可能 在一种情况下对您有用,而在另一种情况下却对您不起作用,或者更糟的是,也许它们也过时了!您应该通过在它们的位置提供虚拟函数来验证问题出在您认为的地方。那毕竟是creating an MCVE so that your question doesn't get closed.

的一部分过程

不,我不会对这个问题感兴趣,因为它的质量极差;正如我之前提到的,这个问题依赖于正确编译语法错误的代码,因此我们无法重现您看到的结果。即使我们修复了语法错误,我们也必须填补空白(这是您的工作),这会在任何可能的答案中引入不确定性方面。关于这个问题,我唯一感兴趣的是让它关闭的过程 closed.