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

时间:2015-06-22 03:46:19

标签: c memory-management malloc

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

这是我的代码:

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()用于提示用户决定是否继续进行单词搜索。

################################################## ################### 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,'\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;。

总结一下,我的常规是:

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

    重复一遍。

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

    至于testfile.txt,我在评论区发布了以下链接。感谢

1 个答案:

答案 0 :(得分:4)

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

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

  • 您为n1分配buff个字节指向。偶然的机会,你碰巧知道多少个字节?也许您应该检查,然后自己回答:您可以在该对象中存储多少字节?
  • 然后您尝试将n2字节读入buff。哪个更高,n1n2?你看过那个吗?如果你试图将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,这并不是必需的string(因为它使用strncat)。 更多未定义的行为!这真是令人惊讶吗?

上一个回答

首先,这段代码甚至无法编译。您在inputWord(Node,newWord,totalNumberOfTheWord)prompt之后立即错过了分号。也许您还没有注意到这些错误,并且您运行的是我们没有源代码的过时二进制文件?

其次,即使要编译此代码,也有许多undefined behaviour的实例,例如:

  • malloc返回NULL并且您尝试修改 NULL指向的对象时,会出现空指针解除引用。例如BSTnode* firstNode=malloc(sizeof(BSTnode));紧随其后firstNode->leftchild=NULL;。也许您可以这样声明firstNodeBSTnode firstNode = { 0 };并使用&firstNode创建指向它的指针...毕竟,你真的应该选择最合适的存储持续时间而不是默认每次都要malloc 。在这方面,我强烈建议将分配逻辑与数据结构逻辑分开;如果您需要进一步详细说明,请考虑scanf的设计方式。
  • fflush(stdin);。每当您第一次使用某个功能时,您应该始终非常仔细地阅读和理解本手册 ......而且这不仅仅是为了提供有关如何设计功能的见解。如果您在使用fflush之前已经阅读并完全理解 this fflush manual,那么您将永远不会使用这个有问题的代码。考虑使用scanf("%*[^\n]"); getchar();之类的东西。
  • 在某些地方,您使用%p格式指令,该指令要求void *指针作为相应的参数。但是,您提供的相应参数的类型为struct BSTnode *。根据{{​​3}},&#34;如果任何参数不是相应转换规范的正确类型,则行为未定义。&#34;

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

这些问题表明您的findWordwordCount功能也不一定值得信赖和万无一失;他们可能会在一个环境中为你工作而在另一个环境中失败,或者更糟糕的是,也许他们也过时了!您应该通过在其位置提供虚拟功能来验证问题是您认为的问题。毕竟,这是the fprintf manual

的部分过程

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