6

私は私が乗り越えることができないエラーにかなり絶望的です。

大学のCプログラミングクラスでは、GML(Graph Modeling Language)入力ストリームのパーサーを実装する必要がありました。

成功すると、パーサーは抽象データ型を呼び出し元に返します。これは、グラフの表現としての隣接行列です。

さて、パーサーは完璧に機能します。ここ数日で私を絶望させる問題はないでしょう。パーサーには、mallocを呼び出す関数呼び出しが1つあります。mallocは、スキャナーがシンボルごとにパーサーに配信するときに頻繁に呼び出されます。ただし、mallocのmemチャンクは、スキャナールーチンを終了する前にfree()を呼び出すことによって常に解放されます。

しかし、パーサーの非常に深いところに致命的な関数呼び出しが1つあります。これは、mallocを使用して構造体を保存するために12バイトのメモリ(3つの整数プロパティ)を予約する関数を呼び出します。構造体は、グラフ内の単一のエッジに関する情報(ソースノード、ターゲットノード、重み)を格納するために必要です。

この呼び出しは2回行われます。初めて、すべてがうまくいきます。次に、gml構文に従って1〜n個のエッジが発生する可能性があるため、コードはwhileループに入ります。ここで、入力ストリームにエッジが見つかっている限り、同じポインターに新しいEdgeStructへのポインターが割り当てられます。ループ内のエッジ認識ルーチンの最初の呼び出しは、合計で2番目です(最初の呼び出しはループに入る前に発生します。maを参照)。mallocがNULLを返すことにより、常に失敗します。

理由がわからない。

それはメモリ不足の問題ではありません。なぜなら、そのプログラムのmain()関数で1000バイトをmallocすると、楽しみのために、正常に動作するからです。

IDEとしてCode::BlocksとDevCPPを使用しています。どちらの場合も、プログラムで同じ問題が発生します。

これが私の主な解析ルーチンです。

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

}

ここで、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;

}

そして今、「エッジ」認識ルーチンの致命的な呼び出し。まず、構造体のヘッダー

#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

実装

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

私は知っています、それはほとんどコードです;)ただ示すために、解放できるすべてのもの、私は解放しました。

私は過去2日間、もちろんここでもスタックオーバーフローで問題をグーグルで検索しました。そして、mallocがnullを返すことに関する何百ものサイト、投稿などがあります。それらはすべて基本的に同じことを言います:十分なメモリがない(つまり、それをありそうもないと呼ぶことができます)、または断片化されたヒープなので、十分なサイズのmemチャンクは利用できません。

しかし:私が要求するのは、3つのintプロパティを格納するための12(つまり、12)バイトだけです。多すぎるようです。

知らない内部制限を超えましたか?

助けていただければ幸いです。

よろしくお願いしますローラ​​ンド

2012年11月24日編集:

あなたの答えに感謝します。しかし。問題はもっと基本的な性質のものでなければなりません。

理由:パーサーよりもはるかに複雑ではなく、main()から1回だけ呼び出すプログラムの他の部分(ファイルI / O)などをテストした場合、mallocもできません。私が読んだファイルはおよそ140バイトです。他のすべてのパーツから分離されたI/Oパーツをテストする場合でも、別のプロジェクトでそれらをアウトソーシングする場合でも、システムからメモリを取得しません。決して。私はコンピュータを再起動しました、すべて。絶対。番号。変化する。

何か案は?その間、私はこのプロジェクトにあまりにも多くの時間を費やしましたが、そのほとんどはそれらのf ***ingメモリエラーを追跡しています...:-(((

4

2 に答える 2

3

アプリケーションが大量のメモリを使用している、または使用可能なすべてのメモリを断片化している疑いがある場合は、実行中にアプリケーションによるメモリ使用量を確認できます。すべてのシステム メモリを消費する場合、malloc はこのため NULL を返します。

上記が当てはまらない場合は、アプリケーションのヒープの破損をチェックします。通常、ヒープ構造を上書きすると、非常に悪いことが起こります。デバッグ モードでは、コンパイラは、ヒープの破損を検出するためにいくつかのチェックとレッド ゾーンを追加します。リリース モードでヒープ構造を破壊すると、通常はアクセス違反が発生します。非常にまれなケースでヒープ構造が破損し、その破損がスペース不足と解釈される可能性があるため、malloc によって NULL が返されることが想像できます。

いずれにせよ、私はメモリデバッガを使用します。Valgrind に何度も助けられましたが、あなたの環境では利用できないかもしれません。ここには、メモリ デバッガーに関するスタックオーバーフローに関する多くのトピックがあります。

于 2012-12-02T22:04:00.057 に答える
1

多くを語ることはできませんが、1 つだけ意見を述べます。ではGetNextSymbol()、読み取る桁数に制限がないため、バッファ オーバーフローが発生する可能性があります。同じことが識別子の読み取りにも当てはまります。

の別の 1 つGraph()、失敗したEdge(sourceString, currentPosition)呼び出しは while ループにあり、結果は決して解放されません、AFAICS。

于 2012-11-17T22:31:57.530 に答える