4

この構造体内の何かを解放しようとすると、問題が発生します。まず、ヘッダーの構造体定義:

    typedef struct{
int* rType;
unsigned long int numCols;
char* rString;  //The literal string, NULL delimited
int rsSize;     //The size of the string, since NULLS can't be used to find the string end
int* colsIndex; //Where each new column starts in rString
long* iColVals;   //integer version of the column
double* dColVals; //double precision value of column
}  row_t ;

次に、構造体のインスタンスを作成できる場所を次に示します。

row_t* delimitLine(char* line, char* delimList, char delimListSize)
{
row_t* thisRow;
.
.
.
//Make a place for this stuff in memory
thisRow = (row_t*) malloc(sizeof(row_t));
if(thisRow==NULL) return NULL;
.
.
.
thisRow->rString = line;

//Make Row Mem

    //colsIndex
    thisRow->colsIndex = (int*) malloc(numCols*sizeof(int));
    if(thisRow->colsIndex==NULL) return NULL;   

    //rType
    thisRow->rType = (int*) malloc(numCols*sizeof(int));
    if(thisRow->rType==NULL) return NULL;   

    //iColVals
    thisRow->iColVals = (long*) malloc(numCols*sizeof(long));
    if(thisRow->iColVals==NULL) return NULL;    

    //dColVals
    thisRow->dColVals = (double*) malloc(numCols*sizeof(double));   
    if(thisRow->dColVals==NULL) return NULL;    
.
.
.
return thisRow;

次に、「線」を作成する方法を次に示します。

char* RBreadLine(fifo_t* fifo)
{
char* outbuf = NULL;
.
.
.
outbuf = (char*) malloc(sizeof(char)*(cnt+1));
.
.
.
return outbuf;
}

最後に呼び出しシーケンス:

main()
{
row_t* row = NULL;
.
.
.
while(kg>=0)
{
//test condition to exit loop not shown
line = RBreadLine(fifo);
.
.
.

 row = delimitLine(line, delimList, delimListSize);
//some code to manipulate the row data here
 printRow(row);
 rowDestructor(row);



}

}

rowDestructor の呼び出しをコメント アウトすると、プログラムは期待どおりに動作しますが、何かを解放しようとするとクラッシュします。構造体の個々のメンバーを除くすべての行にコメントを付けてみましたが、それでもクラッシュするため、ここで何か間違ったことをしています。

アイデアは、行ごとに読み取り、行データを操作するための作業を実行できる大きなテキスト ファイル処理プログラムを用意し、printRow() が最終結果を出力することです。

簡単なテスト ケースは、値が delimitLine 関数 (メモリを割り当て、行構造体にデータを入力するだけ) から出てくる値を出力することでした。

このプロセスが終了したら、メモリを解放して最初からやり直す必要があります。このプログラムは、デストラクタを呼び出さずに、RBreadLine() および delimitLine() への新しい呼び出しがあるたびにこれらのポインタを単純に孤立させた場合、期待どおりに機能します。

rowDestructor() を呼び出すと、最初の行の後で ( rowDestructor() への最初の呼び出しで) プログラムがクラッシュします。

 Now to start outputting line
    1)  2)  3)  4)  5)  6)  7)  8)  9)  10)  (-10)63.116722551236001948     0       0       0       0       1       1       1       1       1       0    1

    Aborted (core dumped)

おそらく、malloc() または free() について理解できないことがありますが、構造体メンバーからエラーやクラッシュを引き起こすことなく有効なデータにアクセスできれば、free はメモリを解放できるはずです。 mallocされています。

おそらく、これらのポインターをあちこちに渡すことで何か悪いことをしているだけかもしれませんが (「行」へのポインターを、それを構造体メンバーに割り当てる関数に渡すなど)、私の心では、それはすべて十分に説明されています。構造体のすべてのメンバーがmallocされることを知っているので、上に進んで構造体を解放するときにすべてを解放できるはずです。次に、行構造体に別のポインターをダンプして、もう一度実行します。

これを行っている理由は、非常に大きなデータ セットを処理できるようにしたいからです。これは、fread を使用してすべてをメモリにロードしてから処理していたプログラムを構造的に書き直したものですが、一部のデータ セットによってコンピュータのメモリが不足するため、ブロック処理アプローチに頼ります。 .

行を正常に解放できたら、row_t** を作成することでこれを構築できます。ここで、FIFO バッファーの概念を使用して行を row_t** に循環させることができます。これにより、テキスト ファイルを前後にシークできます。ただし、ファイル全体をメモリにロードする必要はありません。

たとえば、行 FIFO はこれらの新しい row_t* 構造体を row_t** に格納し、循環バッファーをいっぱいにして古いポインターを上書きし始めた後、古いものを解放します...これが私がこれを使用する場所です。

この質問に対する答えがあれば、私の malloc() と free() の理解の突破口になると思います。あるいは、ポインターと構造体に関する何かが明らかになるかもしれません。

ご意見ありがとうございます。

編集:私の質問の最も重要な部分を見落として申し訳ありません:

void rowDestructor(row_t* thisRow)
{
    //rString
    free(thisRow->rString);

    //colsIndex
    free(thisRow->colsIndex);   

    //rType
    free(thisRow->rType);   

    //iColVals
    free(thisRow->iColVals);    

    //dColVals
    free(thisRow->dColVals);

    //finally kill the whole thing
    free(thisRow);
}

そして、他の人がコンパイラフラグについて言及しているので、私が使用しているものは次のとおりです。

gcc  -Wall laproc.c utils.c csvsurgeon.c -lm -o csv-surgeon

(laproc.c は、数学ライブラリとリンクする必要がある私の特定の信号処理コードです。この例では、これらの関数が呼び出されないように単純化して除外しています)

このバージョンの gcc を使用しています。

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-pc-cygwin/4.5.3/lto-wrapper.exe
Target: i686-pc-cygwin
Configured with: /gnu/gcc/releases/respins/4.5.3-3/gcc4-4.5.3-3/src/gcc-4.5.3/configure --srcdir=/gnu/gcc/releases/respins/4.5.3-3/gcc4-4.5.3-3/src/gc
c-4.5.3 --prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --libexecdir=/usr/lib --datadir=/usr/share --localstatedir=/var --sysco
nfdir=/etc --datarootdir=/usr/share --docdir=/usr/share/doc/gcc4 -C --datadir=/usr/share --infodir=/usr/share/info --mandir=/usr/share/man -v --with-g
mp=/usr --with-mpfr=/usr --enable-bootstrap --enable-version-specific-runtime-libs --libexecdir=/usr/lib --enable-static --enable-shared --enable-shar
ed-libgcc --disable-__cxa_atexit --with-gnu-ld --with-gnu-as --with-dwarf2 --disable-sjlj-exceptions --enable-languages=ada,c,c++,fortran,java,lto,obj
c,obj-c++ --enable-graphite --enable-lto --enable-java-awt=gtk --disable-symvers --enable-libjava --program-suffix=-4 --enable-libgomp --enable-libssp
 --enable-libada --enable-threads=posix --with-arch=i686 --with-tune=generic --enable-libgcj-sublibs CC=gcc-4 CXX=g++-4 CC_FOR_TARGET=gcc-4 CXX_FOR_TA
RGET=g++-4 GNATMAKE_FOR_TARGET=gnatmake GNATBIND_FOR_TARGET=gnatbind --with-ecj-jar=/usr/share/java/ecj.jar
Thread model: posix
gcc version 4.5.3 (GCC)

おそらくそれが私の問題です...他の次のgccバージョンで再コンパイルしました:

Using built-in specs.
COLLECT_GCC=C:\Program Files\CodeBlocks\MinGW-newer\bin\gcc.exe
COLLECT_LTO_WRAPPER=c:/program files/codeblocks/mingw-newer/bin/../libexec/gcc/mingw32/4.5.2/lto-wrapper.exe
Target: mingw32
Configured with: ../../src/gcc-4.5.2/configure --build=mingw32 --enable-languages=c,c++,ada,fortran,objc,obj-c++ --enable-threads=win32 --enable-libgo
mp --enable-lto --enable-fully-dynamic-string --enable-libstdcxx-debug --enable-version-specific-runtime-libs --with-gnu-ld --disable-nls --disable-wi
n32-registry --disable-symvers --disable-werror --prefix=/mingw32tdm --with-local-prefix=/mingw32tdm --enable-cxx-flags='-fno-function-sections -fno-d
ata-sections' --with-pkgversion=tdm-1 --enable-sjlj-exceptions --with-bugurl=http://tdm-gcc.tdragon.net/bugs
Thread model: win32
gcc version 4.5.2 (tdm-1)

そして別バージョン

Using built-in specs.
Target: mingw32
Configured with: ../../gcc-4.4.1/configure --prefix=/mingw --build=mingw32 --enable-languages=c,ada,c++,fortran,objc,obj-c++ --disable-nls --disable-w
in32-registry --enable-libgomp --enable-cxx-flags='-fno-function-sections -fno-data-sections' --disable-werror --enable-threads --disable-symvers --en
able-version-specific-runtime-libs --enable-fully-dynamic-string --with-pkgversion='TDM-2 mingw32' --enable-sjlj-exceptions --with-bugurl=http://www.t
dragon.net/recentgcc/bugs.php
Thread model: win32
gcc version 4.4.1 (TDM-2 mingw32)

バージョン 4.4.1 では、実行中のさまざまな時点でセグメント障害が発生します。数回実行すると、セグ フォールトなしで 1 回も実行されたので、コンパイラに問題がある可能性があります。私は cygwin を使用しているため、おそらくコンパイラはユーティリティとリンカーを混在させています (「/bin」に間違ったディレクトリを使用しています)。

rowDestructor() コードを含めたので、これらの malloc されたポインターで何をしているのかを明確にするのに十分な量を含めたことを願っています。これまでのコメントをありがとう。

私の C 実装に本質的に何か問題がある場合は、修正したいと思います。それまでの間、開発環境をクリーンアップし、すべてのコンポーネントへの正しいパスを確保することでより良い結果が得られるかどうかを確認します。

4

1 に答える 1

3

私のメモリ管理はうまくいったことがわかりました (私が髪を引っ張っていたのも不思議ではありません)。このプログラムを Linux でコンパイルしたところ、すぐにメモリの割り当てで「off by one」エラーが発生し、範囲外のメモリにアクセスしていました。プログラムの障害は、Linux ボックスではすぐに発生しましたが、Windows では、プログラムを終了する前にしばらく実行させていました。

興味のある方のために、ここに問題がありました。

 row_t* delimitLine(char* line, char* delimList, char delimListSize)
{
//<Analyze "line" to count number of data fields>
.
.
.

これはそれが行われていた方法です:

//Populate Row Data 
thisRow->numCols = numCols+1;   

これが修正された方法です:

//Populate Row Data 
numCols+=1;
thisRow->numCols = numCols; 

そして、これがなぜ問題だったのかについてのヒントがあります

.
.
.
    //colsIndex
    thisRow->colsIndex = (int*) malloc(numCols*sizeof(int));
    if(thisRow->colsIndex==NULL) return NULL;   

    //rType
    thisRow->rType = (int*) malloc(numCols*sizeof(int));
    if(thisRow->rType==NULL) return NULL;   

    //iColVals
    thisRow->iColVals = (long*) malloc(numCols*sizeof(long));
    if(thisRow->iColVals==NULL) return NULL;    

    //dColVals
    thisRow->dColVals = (double*) malloc(numCols*sizeof(double));   
    if(thisRow->dColVals==NULL) return NULL;

thisRow->numCols は、プログラムの後半でメモリにアクセスするために使用されていましたが、メモリは「numCols」で割り当てられていたため、プログラムがアクセスしようとしていた要素よりも 1 つ少ない要素でした。

どうやら不正なメモリ アクセスにより、このメモリを解放する際に free() に問題が発生していたようです。興味深いことに、Linux ボックスでは、これによりすぐにセグメンテーション エラーが発生しました。

これは、質問をするときに、「無関係な」コードを投稿することさえ重要になる場合があることを学んだことに起因します。

自力で解決しましたが、ご意見ありがとうございます。明らかな「あなたの欠陥がある」ことを誰も指摘しなかったという事実は、もう少し深く掘り下げて、ここに投稿されていないコードのセクションにコメントするように促しました.

于 2013-06-19T17:47:07.137 に答える