2011-09-09 10 views
1

私はCで円記号のリンクをチェックするプログラムを書いています。戦略は、ファイルのinodeとdevIDを格納するstruct fileInfoを作成することです:Cで奇妙なメモリエラー

typedef struct fileInfo fileInfo; 

struct fileInfo { 
    ino_t inode; 
    dev_t devID; 
}; 

これらの構造体の配列を作成し、新しいファイルを開く前にそのファイルがすでに存在するかどうかを毎回確認します。もしそうなら、それは循環リンクです。

void func1(...) 
{ 

    fileInfo **fileData = malloc(sizeof(struct fileInfo*)); 
    int fileDataLen = 0; 
    char* path = "path of file"; 
    /* some flags */ 

    func2(path, fileData, &fileDataLen); 

    for (int i = 0; i < fileDataLen; i++) 
     free(fileData[i]); 
    free(fileData); 
} 

void func2(char* path, fileInfo ** fileData, int * fileDataLen) 
{ 
    //try to open file 
    struct stat buf; 
    if (openFile(file, &buf, followSymLinks) == -1) 
     exit(1); 

    fileData = checkForLoops(fileData, fileDataLen, &buf, file); 

    if (S_ISDIR(buf.st_mode)) 
    { 
     char* newPath = /* modify path */ 
     func2(newPath,fileData, fileDataLen); 
    } 

    /* other stuff */ 

} 

int openFile(char* file, struct stat * buf, fileInfo ** fileData, int * fileDataLen) 
{ 

    if (lstat(path, buf) < 0) 
     { 
      fprintf(stderr, "lstat(%s) failed\n", path); 
      return -1; 
     } 
    return 0; 
} 

fileInfo** checkForLoops(fileInfo **fileData, int * fileDataLen,struct stat *buf, 
        char* path) 
{ 
    for (int i = 0; i < (*fileDataLen); i++) 
    { 
     if (fileData[i]->inode == buf->st_ino && 
      fileData[i]->devID == buf->st_dev) 
      fprintf(stderr, "circular symbolic link at %s\n", path); 
    } 


    fileInfo *currFile = malloc(sizeof(struct fileInfo)); 
    memcpy(&currFile->inode, &buf->st_ino, sizeof(buf->st_ino)); 
    memcpy(&currFile->devID, &buf->st_dev, sizeof(buf->st_dev)); 

    fileData[(*fileDataLen)] = currFile; 
    (*fileDataLen)++; 
    fileData = realloc(fileData, ((*fileDataLen)+1) * sizeof(struct fileInfo*)); 

    return fileData; 
} 

しかし、func2()をいくつか呼び出した後にメモリリークがあり、fileDataが何も指していないことに気付きました。私はfunc2()で何も解放しないので、漏れがどこから来ているのか分かりません。私は何かがあると仮定していますrealloc shenanigans、しかし私はなぜ理解していない。ヘルプは非常に高く評価されるだろう!

+0

メモリリークを見つけるために[valgrindの(http://valgrind.org/docs/manual/quick-start.html#quick-start.mcrun)を使用してみてください。参考までに、 'fileData = calloc(sizeof(struct fileInfo *)、n)'を使って配列をあるサイズの 'n 'に初期化する方がはるかに効率的です。次に、配列の空き領域が足りなくなったら、 'n * 2'にreallocします。このようにして、ファイル情報を追加するたびに再割り当てを避けることができます。 – Chris

答えて

2

私はコード内のカップル奇妙に気付いています。

まず、openFileの関数シグネチャはvoidの戻り値型を持って、まだあなたはここで、戻り値をチェック:ピーターも指摘するように、あなたが十分に配分していない、

if (openFile(file, &buf, fileData, fileDataLen) < 0) 

第二にreallocを呼び出しスペース:意味(*fileDataLen) == 0は、*fileDataLenをインクリメントした後、あなたが今だけ1の値を持つ最初の繰り返し、オン

fileData = realloc(fileData, (*fileDataLen) * sizeof(struct fileInfo*)); 

割り当てられた配列のサイズが変更されていないため、何も割り当てられていない(つまり、fileDataが既に指していたメモリを単に返すだけです)。したがって、次に別の再帰呼び出し中にfileData[(*fileDataLen)] = currFile;を呼び出すと、currFileの値がfileData[1]にコピーされますが、そのメモリはまだ割り当てられていません。また、reallocが呼び出される次回は、より長い、同じ位置でのメモリを再割り当てなくてもよいので、fileDataオーバーコピーのみ最初の配列エントリに、完全に異なる場所を指すであろう。

第三に、あなたはメモリが指している場所の値を変更している、とあなたは元fileData変数の実際のメモリアドレスを渡していない、あなたのfunc2()関数内reallocを呼び出すことによって、あなた以来func1()free(fileData)を呼び出すことはできませんあなたのfunc2()関数を参照してください。言い換えればのfunc1()への呼び出しでletの値が0x10000と返され、その割り当てられたメモリのどこか他の場所にreallocが呼び出された場合、0x10000に割り当てられたメモリは他の場所に移動しましたが、fileDatafunc1()のローカルスコープのコンテキストではまだ0x10000です。したがってfree(fileData)に電話をかけたときに起こっているfree(0x10000)を効果的に呼び出すと、配列のメモリが0x10000に割り当てられなくなるため、エラーが発生します。配列を解放するためには、いずれかに変更する必要がありますfunc2()openFile()の機能の署名を意味し、func2()にすべての再帰呼び出しからポインタ配列に更新ポインタを返す、または参照によりfileDataを渡すために持ってしようとしていますfileInfo***タイプ、およびfunc2()fileDataopenFile()にアクセスするたびに、あなたはまた、間接の余分な層が必要になります。あなたはどこか他のreallocを呼び出すときに、あなたが実際にそれが同様にfunc1()に割り当てられた、そのポインタにfree()を呼び出すことができるようfileDataの値を変更しています。

最後に、あなただけのfileDataに割り当てられたメモリを解放した場合、あなたはfileDataノードへのポインタの配列のみだったので、ヒープに割り当てられたすべてのfileInfoのノードのための大きなメモリリークを持ってしようとしていることを、心に留めておきます実際のノード自体ではありません。

+0

+1追加の問題を見つける。 –

+0

すばらしいポストをありがとう。実際のコードをより正確に反映して、あなたの提案を組み込むようにコードを更新しました。しかし、私が得ている問題は、fileDataがまだreallocされたメモリブロックを指していないということです。私はそれを正しくやっているように感じますが、新しいコードに問題はありますか? – moose

+0

参照を渡していないので、 'fileData'への参照が失われています。参照によって関数に 'fileInfo **'を渡すためには、 'fileInfo * 'が必要な' fileData'のアドレスを 'func2()'と 'openData()' ...にする必要があります。 ** argument-type、および 'func2()'と 'openData()'の中で 'fileData'を使って作業するときに間接的な層を追加します。 – Jason

1

あなたの問題は、あなたが fileDataのための十分なメモリを割り当てていないことである。

のfileInfo * FILEDATA = malloc関数(はsizeof(構造体のfileInfo));ここで

あなただけを使用しているように見えるの代わり fileInfoインスタンスの配列で、 fileInfo単一のポインタのためにメモリを割り当てます。

申し訳ありませんが、私の最初の考えは間違っていた...しかし、あなたの問題はまだあなたがfileDataのための十分なメモリを割り当てていないことのようです - ちょうど別の場所に:

ここ
fileData[(*fileDataLen)] = currFile; // 1 
(*fileDataLen)++; 
fileData = realloc(fileData, (*fileDataLen) * sizeof(struct fileInfo*)); // 2 

あなたは1を割り当てます要素は必要以上に少なくなります。最初はfileDataLenが0、fileDataが1要素で始まります。最初のファイルを開いたら、fileDataLenを1に増分し、次に2の代わりに1つの要素を含むように配列を再割り当てします。したがって、2番目のファイルを開くとき、あなたのバッファーは上記の// 1でオーバーランし、いくつかのメモリーは上書きされます。

あなたはサイズfileDataLen + 1に配列を再割り当て、すべての回で、この不変を保つ必要があります。

fileData = realloc(fileData, (*fileDataLen + 1) * sizeof(struct fileInfo*)); 
+0

ああ、本当にありがとう - これは私の一部で大きな間違いでした!それを修正した後、ポインタの格納はうまく動作しますが、私は 'free(fileData)'に無効な空きがあります。なぜどんなアイデア? – moose

+0

@mose私の答えの3番目のポイントを参照してください... – Jason

0

私はあなたが関数func2にパス変数を介して行っているが、あなたは、文字列のこれらの種類は、に格納されているので、別のメモリの問題にあなたを導くでしょう静的な文字列を変更しようとしている可能性があります処理の種類がわかりませんOSによって予約されたプライベートメモリゾーン。

+0

文字列のいずれも静的ではありません。 – moose

+0

申し訳ありませんが、私はこの行で混乱しました。 'char * path =" file "のパス"ですが、これは単なる例であったと思います。 – Juan