- ベストアンサー
レコード件数、レコード長の分からない文字列配列の定義の仕方
あるファイルを読み込み、文字列配列に格納しようと思うのですが、 データが数件の場合もあれば、数万件の時もあるので、 数件の時にも数十万件の領域を確保しておくのは抵抗があります。 そこで一度読み込んでレコード件数、レコード長を取得し callocやmallocなどを使い、領域を定義しようと思うのですが、 どのように定義すればよいかわかりません。 char **testData; を定義してmallocを使用しようと思いましたが、その方法を教えてください。
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
だいたい以下のような感じだと思います。 mallocの戻り値チェックは省いていますが、 実際はNULLかどうかのエラーチェックをした方がよいです。 また、sizeof(char)は1なので省くこともできます。 -------------------------------- char **testData; testData = (char **) malloc( sizeof(char *) * レコード件数 ); for(i=0; i<レコード件数; i++){ testData[i] = (char *) malloc( sizeof(char) * レコード長 ); /*testData[i]にi番目のレコードの内容を入れる*/ } /*testDataを使う*/ for(i=0; i<レコード件数; i++){ free(testData[i]); } free(testData); -------------------------------- > #1 > char **testData; // これは char *testData[1]; でも同じ char *testData[1] だと testDataは「charポインタの要素数1の配列」になります。 配列は左辺値になれないので、 最初の testData = (char**)malloc(sizeof(char*) * 10); が失敗してしまうと思います。
その他の回答 (6)
- Wakkey-san
- ベストアンサー率44% (85/191)
> #5 申し訳ないです、私の勘違いでした。 昔のソース引っ張り出してみてみたらいったん**ptrで確保したメモリのポインタをmeemcpyで直接コピーしてました…。 お騒がせしました。
- Werner
- ベストアンサー率53% (395/735)
#がずれてる( ̄□ ̄;) #5の文中の 最初の 「> #5」は「> #3」、 次の「> #3」は「> #4」の間違いです。m(_ _)m
- Werner
- ベストアンサー率53% (395/735)
#2です。 > #5 reallocの第一引数がNULLの時はmallocと同じ働きなのを利用すれば、 「初めてならメモリ確保」と「メモリの再確保」の場合分けを統合できそうですね。 こっちは、本題からずれてしまいますが、どうしても気になるので。すみません^^; > #3 > 配列変数のカッコを取るとポインタと同様に機能します。 > 一度実際にコンパイルしてみてください。 う~ん、#2も念のためコンパイルして失敗することを確認してから投稿したのですが。 しかし、「配列変数のカッコを取るとポインタと同様に機能します」ということなので 私が知らないだけでうまくいくのかもしれません。 ちょっと、気になるのでコンパイルできるソースを提示できるならお願いしたいです。 (配列変数のカッコというのがどれのことなのかよく分からなかったので。) なお、私は以下のようなコードで確認してます。 testData1は配列なのでポインタであるtestDataと同様には扱えずエラーになります。 -------------------------------------------- #include<stdio.h> #include<stdlib.h> int main(void){ char **testData; char *testData1[1]; /*問題なし*/ testData = (char**)malloc(sizeof(char*) * 10); testData[0] = (char*)malloc(100); /*↓Error: testData1は配列なので左辺値になれない*/ testData1 = (char**)malloc(sizeof(char*) * 10); /*↓これだけなら問題なし。ただし、testData1[1]以降は使えない。*/ testData1[0] = (char*)malloc(100); return 0; }
- Wakkey-san
- ベストアンサー率44% (85/191)
追記です。 文字列を扱う場合は mallocより 作成領域をゼロで初期化する calloc の方が都合がいいです。 strcmpに渡す場合などにコピー先、コピー元とも両方バッファ内にNULL文字が含まれていない(初期化されていない)とアクセス違反を起こします。 △ testData[0] = (char*)malloc(100); ZeroMemory( testData[0], 100 ); // ゼロクリアしてバッファ初期化 // 手抜きで *(testData[0]) = '\0'; もありますが ○ testData[0] = (char*)cmalloc( 1, 100 ); // 領域はすでにゼロクリアされている > #2 >> char **testData; // これは char *testData[1]; でも同じ > char *testData[1] だと > testDataは「charポインタの要素数1の配列」になります。 > 配列は左辺値になれないので、 > 最初の > testData = (char**)malloc(sizeof(char*) * 10); > が失敗してしまうと思います。 配列変数のカッコを取るとポインタと同様に機能します。 一度実際にコンパイルしてみてください。
- tettsu
- ベストアンサー率30% (4/13)
struct record { char *m_pData; }; int main(int argc, char* argv[]) { int record_count = 0; char buf[1000]; FILE *fp; struct record * pRecordSet = NULL; int i; //読込 fp = fopen("test.dat", "r"); if (fp == NULL){ return 0; } while ( fgets(buf, 999, fp) != NULL){ size_t len = strlen(buf); if (len == 0){ //空なら飛ばす continue; } record_count++; if (pRecordSet == NULL){ //初めてならメモリ確保 pRecordSet = (struct record *)malloc(sizeof (struct record)); } else { //メモリの再確保 pRecordSet = (struct record *)realloc(pRecordSet, sizeof (struct record) * (record_count+1) ); } (pRecordSet + (record_count-1))->m_pData = (char *)malloc( sizeof ( char ) * (len + 1)); strcpy((pRecordSet + (record_count-1))->m_pData, buf); } fclose(fp); //表示 for (i = 0; i < record_count; i++){ printf("%d: %s\n", i, (pRecordSet + i)->m_pData); } //開放 for (i = 0; i < record_count; i++){ free( (pRecordSet + i)->m_pData ); } free(pRecordSet); return 0; } C言語ならこんな感じで良いのではないでしょうか。 C++ならSTLのvectorを使うと簡単です。
お礼
structを使用した方が応用がききそうですね。 参考になります。ありがとうございました。
- Wakkey-san
- ベストアンサー率44% (85/191)
動的なポインタ配列を作成したいのであれば、 char **testData; // これは char *testData[1]; でも同じ char cpy_data[1024]; testData = (char**)malloc(sizeof(char*) * 10); // char型ポインタ変数を10個分確保する testData[0] = (char*)malloc(100); // 1個目のポインタにメモリ領域作成 testData[1] = (char*)malloc(200); // 2個目のポインタにメモリ領域作成 : testData[9] = (char*)malloc(200); // 10個目のポインタにメモリ領域作成 と処理し、あとは strcpy( cpy_data, testData[1] ); のように文字列配列と同様に使用できます。 なお、プログラム終了等で動的メモリが全て不要になった場合は先にポインタ配列の中身をすべてfreeしてから最後に配列本体をfreeする必要がある点にご注意ください。
お礼
なるほど、callocとmallocの違いは値を入れてしまえば 関係ないぐらいにしか思っていませんでした。 この場合はcallocの方がいいですね。