简体   繁体   English

为什么 malloc 使用大量内存返回 NULL 并且总是在同一点?

[英]Why does malloc return NULL with plenty of memory and always at the same point?

i am quite desparate on an error i am not able to get over.我对一个我无法克服的错误感到非常绝望。

For my C programming class at university, i had to implement a parser for GML (Graph Modeling Language) input streams.对于我在大学的 C 编程课,我必须为 GML(图形建模语言)输入流实现一个解析器。

On success, the parser returns an Abstract Datatype to the caller, which is an Adjacency Matrix as representation of the graph.成功时,解析器向调用者返回一个抽象数据类型,它是一个邻接矩阵,作为图的表示。

Okay, the parser works flawlessly, wouldnt there be a problem reducing me to despair over the last few days.好的,解析器完美无缺,在过去的几天里不会有问题让我感到绝望。 In the parser, there is one function call which in turn calls malloc.在解析器中,有一个函数调用依次调用 malloc。 malloc is called quite often during the scanner delivers symbol by symbol to the parser. malloc 在扫描器将一个符号一个符号传递给解析器的过程中经常被调用。 But the malloc´d mem chunks are ALWAYS freed by calling free() before leaving the scanner routine.但是 malloc 的内存块总是在离开扫描程序之前通过调用 free() 来释放。

But there is one fatal function call quite deep inside the parser, which, in turn calls a function that uses malloc to reserve 12 bytes of memory (three integer properties) for saving a struct.但是在解析器内部有一个致命的函数调用,它反过来调用一个函数,该函数使用 malloc 保留 12 字节的内存(三个整数属性)以保存结构。 The struct is needed to store information about a single edge within the graph (source node, target node, weight).需要该结构来存储有关图中单个边的信息(源节点、目标节点、权重)。

This call is made two times.此调用进行了两次。 the first time, all goes well.第一次,一切顺利。 Then, as there can occur 1 to n edges according to the gml syntax, the code enters a while loop, where the same pointer is assigned with a pointer to a new Edge Struct, as long as there are Edges found in the input stream.然后,由于根据 gml 语法可能会出现 1 到 n 条边,因此代码进入 while 循环,只要在输入流中找到边,就会为相同的指针分配一个指向新边结构的指针。 The first call of the Edge recognition routine within the loop, wich is the second in total (the first one happens before entering the loop, see ma), fails constantly by malloc returning NULL.循环中边缘识别例程的第一次调用,总共是第二次调用(第一个发生在进入循环之前,参见 ma),由于 malloc 返回 NULL 不断失败。

I simply have no idea why.我只是不知道为什么。

Its not about a memory shortage issue, because when i malloc 1000 bytes in the main() function of that program, just for fun, it works fine.这与内存不足问题无关,因为当我在该程序的 main() 函数中 malloc 1000 个字节时,只是为了好玩,它工作正常。

I use Code::Blocks and DevCPP as IDEs.我使用 Code::Blocks 和 DevCPP 作为 IDE。 In both, the program encounters the same problem.在这两种情况下,程序都遇到了同样的问题。

Here´s my main parsing routine:这是我的主要解析例程:

DirectedGraph Graph(char* sourceString, int*currentPosition){

 int sym;
 int restartPosition = 0;
 int* backupPosition;
 char* backupString;
 int nodeCount = 0;

 int currentSrc = -1;
 int currentTgt = -1;
 int currentWgt = -1;
 EdgeDescription e;
 DirectedGraph correctMatrix;
 MatrixStruct* errorMatrix = NULL;

 /*begin parsing*/
 bool isGraphHeader = GraphHdr(sourceString, currentPosition);

 if(isGraphHeader == true){

  bool isNode = Node(sourceString, currentPosition);

  if(isNode == true){

     while(isNode == true){

        nodeCount++;
        restartPosition = *currentPosition;
        isNode = Node(sourceString, currentPosition);

     }

     *currentPosition = restartPosition;

     /*now get edge information (from-to-weight)*/
     /*as we have already read the next symbol, we have to reset*/
     /*our read position by one symbol backwards*/
     e = Edge(sourceString, &restartPosition);  /*<======== HERE I CALL THE FATAL ROUTINE FOR THE FIRST TIME - EVERYTHING´s JUST FINE, PROGRAM PROCEEDS*/
     restartPosition = 0;

     /*just for clearer coding in if statement*/
     currentSrc = e->source;
     currentTgt = e->target;
     currentWgt = e->weight;
     destroyEdge(e);

     if(currentSrc != -1 && currentTgt != -1 && currentWgt != -1){

        /*initialize matrix with counted number of nodes*/
        correctMatrix = CreateNewGraph(nodeCount);

        /*the edge is inserted only when it lies within the boundaries*/
        /*of our graph. but we do not interrupt the whole processing, we just skip it.*/
        while(currentSrc != -1 && currentTgt != -1 && currentWgt != -1){

           if(currentSrc <= nodeCount && currentTgt <= nodeCount){

              InsertEdge(correctMatrix, currentSrc, currentTgt, currentWgt);
              restartPosition = *currentPosition;
           }

           e = Edge(sourceString, currentPosition); /* <============== THIS IS THE CALL THAT FAILS*/
           currentSrc = e->source;
           currentTgt = e->target;
           currentWgt = e->weight;

        }

        /*as we have read over the next symbol in the loop, reset the position to read*/
        *currentPosition = *currentPosition - 1;
        sym = GetNextSymbol(sourceString,currentPosition);

        if(sym == rightBrace){

           sym = GetNextSymbol(sourceString, currentPosition);

           if(sym == eot){

              return correctMatrix;
           }
           else{
              return errorMatrix;
           }
        }
        else{
           return errorMatrix;
        }
     }
     else{
        return errorMatrix;
     }
  }
  else{
     return errorMatrix;
  }
 }
 else{
    return errorMatrix;
 }

} }

Here the GetNextSymbol (the scanner, that delivers the symbols to the parser):这里是 GetNextSymbol(扫描器,将符号传递给解析器):

/**
* DOCUMENTATION
* ============================
* This is the main scanning function
* which is used by the parser to recognize
* terminal symbols and valid literals.
*
* RETURNS: the enum code for the recognized symbol.
* or an error code, when invalid symbol encountered.
*/

int GetNextSymbol(char* sourceString, int* currentPosition){


   int symbolCode;
   int loopCounter = 0;
   char* currentIdentifier = (char*)malloc(10);
   char* currentNumber = (char*)malloc(10);
   int identifierPosition = 0;
   int numberPos = 0;
   int numericVal = 0;
   char currentChar;

   currentChar = getNextChar(sourceString, currentPosition);

    /*skip all blanks, empty chars,
    linefeeds, carriage returns*/
   while(currentChar == ' '
         || currentChar == 11
         || currentChar == 10
         || currentChar == 13
         || currentChar == '\t')
   {
      currentChar = getNextChar(sourceString, currentPosition);
   }

   /*=====================================*/
   /*Section 1: scan for terminal symbols */
   /*====================================*/

   if(currentChar == '['){
      symbolCode = leftBrace;
   }
   else if(currentChar == ']'){
      symbolCode = rightBrace;
   }

   /*=====================================*/
   /*Section 2: scan for valid literals  */
   /*====================================*/

   else if(isdigit(currentChar)){

      /*here we calculate the numeric value of a number expression*/
      /*when calculated, we assign the numeric value to the symCode variable*/
      /*this works out because the values for a real symbol are always negative*/
      symbolCode = digit;
      while(isdigit(currentChar)){

         currentNumber[numberPos] = currentChar;
         currentChar = getNextChar(sourceString, currentPosition);
         loopCounter++;
         numberPos++;
      }

      currentNumber[numberPos] = '\0';
      numericVal = atoi(currentNumber);
      symbolCode = numericVal;

      /*when identifier or braces follow number without space: reset currentPos*/
      /*to the position of the previous char*/
      if(isalpha(currentChar)){
         *currentPosition = *currentPosition - loopCounter;
      }
      else if(currentChar == ']'){
         *currentPosition = *currentPosition - loopCounter;
      }
      else if(currentChar == '['){
         *currentPosition = *currentPosition - loopCounter;
      }

   }
   else if(isalpha(currentChar)){

      while(isalpha(currentChar)){

         currentIdentifier[identifierPosition] = currentChar;
         currentChar = getNextChar(sourceString, currentPosition);
         loopCounter++;
         identifierPosition++;
      }

      /*check wether we have found a valid identifying label*/
      /*and deallocate the reserved mem space*/
      currentIdentifier[identifierPosition] = '\0';
      symbolCode = recognizeIdentifier(currentIdentifier);

      /*when number or braces follow identifier without space: reset currentPos*/
      /*to the position of the previous char*/
      if(isdigit(currentChar)){
         *currentPosition = *currentPosition - 1;
      }
      else if(currentChar == ']'){
         *currentPosition = *currentPosition - 1;
      }
      else if(currentChar == '['){
         *currentPosition = *currentPosition - 1;
      }

   }
   else if(currentChar=='\0'){

      symbolCode = eot;
   }
   /*neither terminal symbol nor end of text found on current position --> illegal symbol*/
   else{
      symbolCode = error;
   }

   free(currentIdentifier);
   free(currentNumber);
   return symbolCode;

}

and now the fatal call in the "Edge" recognition routine.现在是“边缘”识别例程中的致命调用。 First, the header for the struct首先,结构体的头部

#ifndef GML_EDGE_STRUCT_H_INCLUDED
#define GML_EDGE_STRUCT_H_INCLUDED


typedef struct EdgeStruct* EdgeObj;

typedef struct EdgeStruct {

   int source;
   int target;
   int weight;

} EdgeStruct;

typedef EdgeObj EdgeDescription;

EdgeDescription createNewEdge(int src, int tgt, int wgt);
void destroyEdge(EdgeObj);

#endif // GML_EDGE_STRUCT_H_INCLUDED

The implementation实施

#include "GML_EDGE_STRUCT.h"
#include <stdio.h>
#include <stdlib.h>
EdgeDescription createNewEdge(int source, int target, int weight){

   EdgeDescription e;
   int bytesRequested = sizeof(EdgeStruct);

   e = malloc(bytesRequested);
   e->source = source;
   e->target = target;
   e->weight = weight;
   return e;
}

I know, that´s pretty much code ;) Just to show, that everything that can be freed, i freed.我知道,这几乎是代码 ;) 只是为了表明,所有可以释放的东西,我都释放了。

I googled my problem for the last two days, of course also here at stack overflow, and there are hundreds of sites, postings et cetera concerning malloc returning null.过去两天我在谷歌上搜索了我的问题,当然也在这里堆栈溢出,并且有数百个站点、帖子等等关于 malloc 返回空值。 They all say basically the same: not enough memory (which is, lets call it unlikely), or fragmented heap, so there are no mem chunks of sufficient size available.他们都说基本相同:没有足够的内存(也就是说,我们称之为不太可能),或者碎片化的堆,所以没有足够大小的内存块可用。

but: all i request for are 12 (in words: twelve) bytes to store three int properties.但是:我要求的只是 12 个(字数:12 个)字节来存储三个 int 属性。 which seems to be too much.这似乎太多了。

Have i exceeded some internal limits i don´t know about?我是否超出了一些我不知道的内部限制?

Help would be greatly appreciated.帮助将不胜感激。

Thanks in advance Roland提前致谢罗兰

EDIT 2012-11-24:编辑 2012-11-24:

thanks for your answers.感谢您的回答。 but.但。 the problem must be of more basic nature.这个问题必须具有更基本的性质。

because: when i tested the other parts of my program (file I/O) etc. which are far less complex than the parser and go just one call deep from the main(), i also cannot malloc.因为:当我测试程序的其他部分(文件 I/O)等时,这些部分远没有解析器复杂,并且只从 main() 深处调用一次,我也无法进行 malloc。 The file i read has roughly 140 bytes.我读取的文件大约有 140 个字节。 Even when i test the I/O part isolated from all the other parts, even when i outsource them in a different project, i get no memory from the system.即使当我测试与所有其他部分隔离的 I/O 部分时,即使我将它们外包到不同的项目中,我也无法从系统中获得任何内存。 by no means.绝不是。 i´ve restarted the computer, everything.我已经重新启动计算机,一切正常。 absolutely.绝对地。 no.不。 change.改变。

any ideas?有任何想法吗? in the meantime i´ve put far too many hours in this project, the most of which are tracking those f***ing memory errors... :-(((与此同时,我在这个项目中投入了太多时间,其中大部分时间都在跟踪那些该死的内存错误...... :-(((

If you suspect you application of using too much memory or fragmenting all available memory then you can check memory usage by your application while running.如果您怀疑您的应用程序使用了过多内存或将所有可用内存碎片化,那么您可以在运行时检查应用程序的内存使用情况。 If it eats all system memory then malloc returned NULL because of this.如果它吃掉所有系统内存,那么 malloc 会因此返回 NULL。

If the above is not your case then I would check your application for heap corruption.如果上述情况不是您的情况,那么我会检查您的应用程序是否存在堆损坏。 Usually when you overwrite heap structures very bad things happen.通常,当您覆盖堆结构时,会发生非常糟糕的事情。 In debug mode compiler adds some extra checks and red zones for heap corruption detection.在调试模式下,编译器为堆损坏检测添加了一些额外的检查和红色区域。 In release mode breaking heap structures usually lead to access violation.在释放模式下,破坏堆结构通常会导致访问冲突。 I can imagine that in very rare cases heap structures can be damaged and the damage can be interpreted as out of space so NULL is returned by malloc.我可以想象,在极少数情况下,堆结构可能会损坏,并且损坏可以解释为空间不足,因此 malloc 会返回 NULL。

One way or another I would employ memory debugger.我会以一种或另一种方式使用内存调试器。 Valgrind saved me many times, but it may not be available in your environment. Valgrind 救了我很多次,但它可能在你的环境中不可用。 There are many topics here on stackoverflow about memory debuggers. stackoverflow 上有很多关于内存调试器的主题。

I can't say much, just one observation.我不能说太多,只是一个观察。 In GetNextSymbol() , I see no restriction on the number of digits read, so there's the possibility of a buffer overflow.GetNextSymbol() ,我看到读取的位数没有限制,因此可能会出现缓冲区溢出。 The same goes for reading an identifier.读取标识符也是如此。

Another one in Graph() , the failing Edge(sourceString, currentPosition) call is in a while loop and the result is never freed, AFAICS. Graph()另一个,失败的Edge(sourceString, currentPosition)调用处于 while 循环中,结果永远不会被释放,AFAICS。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM