K.Maebashi's BBS

ご自由に書き込んでください。雑談も可。
テスト書き込みの類はテスト用掲示板にどうぞ

[日付順表示] [日付順インデックス] [スレッド順インデックス]

新規投稿 | 開設者ホームページへ戻る | ヘルプ

[534] reallocについて
投稿者:コダマン
2007/02/20 02:13:25

始めまして。 typedef struct{ char cString[50]; char *cpItem[]; //タイプ/処理名/置換先項目/置換元項目/デフォルト値/ }typChangePrm; ↑のような構造体の中のポインタ配列のreallocの方法がわからず苦戦しております。 どなたか、ご存知でしたら教えてください。
[この投稿を含むスレッドを表示] [この投稿を削除]
[535] Re:reallocについて
投稿者:(ぱ)
2007/02/20 02:13:25

はじめまして。 >typedef struct{ > char cString[50]; > char *cpItem[]; //タイプ/処理名/置換先項目/置換元項目/デフォルト値/ >}typChangePrm; まず質問ですが、コンパイラはなんでしょうか? いわゆるANSI-C (ISO-C89)では上のコードは通りません。 ISO-C99なら通ります。gccにも似たような独自拡張があったような。 >↑のような構造体の中のポインタ配列のreallocの方法がわからず苦戦しております。 で、単にcpItemを可変個確保したいだけなら、このページが参考になるでしょう。 http://seclan.dll.jp/c99d/c99d04.htm#dt19990726 確保するサイズは sizeof(typChangePrm) + sizeof(char*) * num ですね。 ただ、それ以前の問題として、こういう構造体をrealloc()する時は、 「この構造体を指しているポインタはないか」ということを意識する必要があります。 realloc()するとアドレスが変わることがあるため、これを指しているポインタがあると そっちも書き換えてやらなければいけません。 それが難しいのなら、可変長構造体を使うのではなく、この構造体に 可変長配列へのポインタを持たせるべきでしょう。 typedef struct{ char cString[50]; char **cpItem; // この先の領域をrealloc()する。 }typChangePrm; # ところでcpItemの数はどっかで保持してるんでしょうか。
[この投稿を含むスレッドを表示] [この投稿を削除]
[536] Re:reallocについて
投稿者:まきじ
2007/02/20 02:13:25

#include<stdio.h> #include<stdlib.h> typedef struct{ char cString[50]; char **cpItem; /* ポインタのポインタにする*/ }typChangePrm; int main(void){ typChangePrm data; char str[]="hoge"; int i; /* 10 個の要素を持つポインタの配列を確保 */ data.cpItem = realloc(NULL,sizeof(char*) * 10); for(i = 0;i < 10; i++){ /* *(data.cpItem + i) に 64 個 の要素を持つ char 型の配列を確保 */ *(data.cpItem + i) = realloc(NULL,sizeof(char) * 64); strcpy(*(data.cpItem + i),str); } for(i = 0;i < 10; i++) printf("%s\n",*(data.cpItem + i)); strcpy(str,"foo"); /* ポインタの配列の要素数を 20 個に変更 */ data.cpItem = realloc(data.cpItem,sizeof(char*) * 20); for(i = 0;i < 20; i++){ /* *(data.cpItem + i) に 64 個 の要素を持つ char 型の配列を確保 */ *(data.cpItem + i) = realloc(NULL,sizeof(char) * 64); strcpy(*(data.cpItem + i),str); } for(i = 0;i < 20; i++) printf("%s\n",*(data.cpItem + i)); return 0; }
[この投稿を含むスレッドを表示] [この投稿を削除]
[537] Re:reallocについて
投稿者:(ぱ)
2007/02/20 02:13:25

ええと、これはコダマンさんの質問に対するひとつの解答例、ということで よいでしょうか? ところで私なら、 >*(data.cpItem + i) = realloc(NULL,sizeof(char) * 64); この部分はこう書きます。 >data.cpItem[i] = malloc(sizeof(char) * 64);
[この投稿を含むスレッドを表示] [この投稿を削除]
[538] Re:reallocについて
投稿者:まきじ
2007/02/20 02:13:25

>>data.cpItem[i] = malloc(sizeof(char) * 64); realloc() の第一引数を、NULL にすれば、malloc() と同じ動作をします。 よって、realloc(NULL,sizeof(char) * 64); でも問題ありません。
[この投稿を含むスレッドを表示] [この投稿を削除]
[539] Re:reallocについて
投稿者:かずま
2007/02/20 02:13:25

>realloc() の第一引数を、NULL にすれば、malloc() と同じ動作をします。 >よって、realloc(NULL,sizeof(char) * 64); でも問題ありません。 malloc も realloc も関係ありません。そのプログラム自体に問題があります。 最初の 10個の領域とは別に、新たに 20個の領域を確保していて、最初の 10個の領域は二度と使えません。これをメモリーリークといいます。
[この投稿を含むスレッドを表示] [この投稿を削除]
[540] Re:reallocについて
投稿者:れぷ
2007/02/20 02:13:25

ついでに言うと、2回目のアロケートを以下のように書き直しても   *(data.cpItem + i) = realloc(*(data.cpItem + i), sizeof(char) * 64); realloc失敗時に*(data.cpItem + i)がNULLになってしまってやはりダメですね。
[この投稿を含むスレッドを表示] [この投稿を削除]
[541] Re:reallocについて
投稿者:れぷ
2007/02/20 02:13:25

そういうわけで私流に書き直してみるテスト。 --- #include <stdio.h> #include <stdlib.h> #include <string.h> typedef enum { true = (1 == 1), false = !true } boolean; typedef struct{ char cString[50]; char **cpItem; } typChangePrm; void freeMemory(typChangePrm *data) { int i; printf("freeMemory - start\n"); for(i = 0; data->cpItem[i]; i++) { printf(" free(item) : pointer[%p]\n", data->cpItem[i]); free(data->cpItem[i]); } printf(" free(item container) : pointer[%p]\n", data->cpItem); free(data->cpItem); data->cpItem = NULL; printf("freeMemory - end\n"); return; } boolean allocateMemory(typChangePrm *data, size_t itemSize) { char **prevMemory; size_t allocateSize; int i; printf("allocateMemory - start\n"); if (data->cpItem != NULL) { freeMemory(data); } data->cpItem = calloc(itemSize + 1, sizeof(char *)); // +1して番兵をつける printf(" allocate(item container) : pointer[%p]\n", data->cpItem); for(i = 0;i < itemSize; i++){ data->cpItem[i] = calloc(64, sizeof(char)); if (data->cpItem[i] == NULL) { freeMemory(data); // 初期化に失敗したら壊す return false; } printf(" allocate(item) : pointer[%p]\n", data->cpItem[i]); } printf("allocateMemory - end\n"); return true; } int main(void){ typChangePrm data; char str[]="hoge"; size_t allocateSize; size_t itemSize; int i; data.cpItem = NULL; allocateMemory(&data, 10); allocateMemory(&data, 20); freeMemory(&data); return 0; }
[この投稿を含むスレッドを表示] [この投稿を削除]
[542] Re:reallocについて
投稿者:まきじ
2007/02/20 02:13:25

>最初の 10個の領域とは別に、新たに 20個の領域を確保 *(data.cpItem + i) = realloc(NULL,sizeof(char) * 64); で、要素数が 64 個の char 型配列が新たに、20 個確保されていると云うことですね? それより前で、10 個確保されているので、第一引数に NULL を指定すると、 新たに 20 個確保され10 個分、余分にメモリを消費する。 だから、解放するか、20 個の内 10 個は、前と同じ内容なので、残りの 新たに確保された領域 10 個に対して、malloc() してやれば良いですね。 data.cpItem[10] から領域を新たに確保という事で for(i = 10;i < 20; i++){ *(data.cpItem + i) = realloc(NULL,sizeof(char) * 64); strcpy(*(data.cpItem + i),str); } でどうでしょうか?
[この投稿を含むスレッドを表示] [この投稿を削除]
[543] Re:reallocについて
投稿者:(ぱ)
2007/02/20 02:13:25

>>>data.cpItem[i] = malloc(sizeof(char) * 64); > >realloc() の第一引数を、NULL にすれば、malloc() と同じ動作をします。 >よって、realloc(NULL,sizeof(char) * 64); でも問題ありません。 いや、もちろんそれは知っていますが、スタイルとしてどちらを取るかという 問題です。 もともと、realloc()の第一引数をNULLにすればmalloc()と同じ動作をする、 という仕様は、最初のメモリ確保と2回目以降のメモリ確保とで別々のコードを 記述する手間を避けるためのものでしょう。 ですから、この例で言えば、data.cpItemの指す先を確保した際、 その中身を全部NULLに初期化しておけば、 data.cpItem[i] = realloc(data.cpItem[i],sizeof(char) * 64); と書けば、このポインタの指す先にデータが来るかどうかわからないような 時、重複したコードを書かなくてよくなるわけです。 この仕様自体はそれなりに便利なものだとは思うのですが(でもこれも realloc()の仕様としてはオーバースペックだという意見もありうるわけですが)、 最初から第一引数に定数のNULLを渡すくらいなら、malloc()を使うべきでしょう。 …が、既にかずまさんからツッコミが入ってますが、まきじさんのコードだと、 2回目のrealloc()でも第一引数にNULL渡してますね…ケアレスミスだとは思いますが。 これは私は気付いていませんでした。
[この投稿を含むスレッドを表示] [この投稿を削除]
[544] Re:reallocについて
投稿者:まきじ
2007/02/20 02:13:25

>最初から第一引数に定数のNULLを渡すくらいなら、malloc()を使うべきでしょう。 realloc() の質問でしたので、realloc() にしただけです。 特に深い意味、意図はないです。
[この投稿を含むスレッドを表示] [この投稿を削除]
[545] Re:reallocについて
投稿者:(ぱ)
2007/02/20 02:13:25

>…が、既にかずまさんからツッコミが入ってますが、まきじさんのコードだと、 >2回目のrealloc()でも第一引数にNULL渡してますね…ケアレスミスだとは思いますが。 よく読んでなかったのでここも修正です… 2回目のrealloc()は別に領域拡張しているわけではなく、単なるmalloc()と 同値なので、上の私の記述は見当外れですね。失礼しました。
[この投稿を含むスレッドを表示] [この投稿を削除]
[546] Re:reallocについて
投稿者:れぷ
2007/02/20 02:13:25

要はコダマンさんが、  ・cpItem自体をreallocしたいのか  ・cpItem[n]のほうをreallocしたいのか に尽きるんじゃないでしょうか。 まきじさんのサンプルは前者をrealloc()ですけれど、 後者も行いたい場合はやはりこちらもrealloc()で書いたほうが便利でしょうね。 # あとcpItem自体が縮小した場合の考慮をしないとメモリリークしますね。 # ってここはツッコミ入れなくても大丈夫か(^-^;)
[この投稿を含むスレッドを表示] [この投稿を削除]
[547] Re:reallocについて
投稿者:本多
2007/02/20 02:13:25

>要はコダマンさんが、 > ・cpItem自体をreallocしたいのか > ・cpItem[n]のほうをreallocしたいのか >に尽きるんじゃないでしょうか。 要求をちゃんと定義してもらわずに作業を始めると戻り工数が大きくなりますよねー 私は最初「構造体の中のポインタ配列のreallocの方法」と言ってるので typedef struct{ char cString[50]; char *cpItem[]; }typChangePrm; typChangePrm data; data.cpItem[n] = realloc( data.cpItem[n], new_size); の程度のことを質問してると思ってましたが...(^^) 日本語の解釈って色々できるんですね。
[この投稿を含むスレッドを表示] [この投稿を削除]
[548] Re:reallocについて
投稿者:774RR
2007/02/20 02:13:25

>data.cpItem[n] = realloc( data.cpItem[n], new_size); 皆さん同じコードをかかれていますけど、これってダメコードですよ。 realloc() に失敗したら、旧 data.cpItem[n] がリークしますね。 # 旧値が失われて復旧の手段が無い。 realloc() に失敗する状況でどうリカバリーするかの仕様次第ですけど、 書かない癖つけたほうがいいと思う。
[この投稿を含むスレッドを表示] [この投稿を削除]
[550] Re:reallocについて
投稿者:本多
2007/02/20 02:13:25

こんな感じかなぁ? 要素追加しちゃったけど。 ---- #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct{ int itemCount; char cString[50]; char **cpItem; } typChangePrm; typChangePrm *CreatePrm( char *cString, int itemCount) { typChangePrm *prm = NULL; int i; if ( (prm = malloc( sizeof( typChangePrm))) == NULL) { fprintf(stderr,"ERROR: memory allocation\n"); goto LERROR; } if ( strlen( cString) >= 50 ) return NULL; strcpy( prm->cString, cString); prm->itemCount = itemCount; if ( (prm->cpItem = malloc( sizeof(char *) * itemCount )) == NULL) { fprintf(stderr,"ERROR: memory allocation\n"); goto LERROR; } for ( i=0; i<itemCount; i++) prm->cpItem[i] = NULL; return prm; LERROR: free( prm->cpItem); free( prm); return NULL; } void PrmDestroy( typChangePrm *prm) { int i; for ( i=0; i<prm->itemCount; i++) free(prm->cpItem); free(prm->cpItem); free(prm); } void PrmResize( typChangePrm *prm, int itemCount) { char **tmp; int i; if ( prm->itemCount >= itemCount ) { for ( i=itemCount; prm->i<itemCount; i++) free( prm->cpItem[i]); prm->itemCount = itemCount; return; } tmp = prm->cpItem; if ( (tmp = realloc( tmp, sizeof(char *) * itemCount)) == NULL) { fprintf(stderr,"ERROR: memory allocation\n"); goto LERROR; } prm->cpItem = tmp; for ( i=prm->itemCount; i<itemCount; i++) prm->cpItem[i] = NULL; prm->itemCount = itemCount; return; LERROR: free( prm->cpItem); } void PrmSetItem( typChangePrm *prm, int item_no, char *item) { if ( item_no >= prm->itemCount) return; if ( (prm->cpItem[ item_no] = strdup( item)) == NULL) fprintf(stderr,"ERROR:memory allocation\n"); } void PrmShow( typChangePrm *prm) { int i; printf("********************************\n"); printf("Item Count:%d\n",prm->itemCount); printf("cString :%s\n",prm->cString); for ( i=0; i<prm->itemCount; i++) printf("Item[%d]:%s\n",i,((prm->cpItem[i]==NULL)?"NULL":prm->cpItem[i])); } int main(void) { typChangePrm *prm; char *item[] = { "hello", "world", "program"}; if ( (prm = CreatePrm("c-string",2)) == NULL) return 1; PrmSetItem(prm,0,item[0]); PrmSetItem(prm,1,item[1]); PrmShow( prm); PrmResize( prm, 3); PrmSetItem(prm,2,item[2]); PrmShow( prm); PrmResize( prm, 1); PrmShow( prm); PrmDestroy(prm); return 0; }
[この投稿を含むスレッドを表示] [この投稿を削除]
[551] Re:reallocについて
投稿者:(ぱ)
2007/02/20 02:13:25

>typChangePrm *CreatePrm( char *cString, int itemCount) >{ > typChangePrm *prm = NULL; > int i; > > if ( (prm = malloc( sizeof( typChangePrm))) == NULL) { > fprintf(stderr,"ERROR: memory allocation\n"); > goto LERROR; ←ココ > } (中略) > LERROR: > free( prm->cpItem); > free( prm); > return NULL; >} 矢印の箇所でgotoしたとき、prmはNULLだからSegmentation faultになりませんか。
[この投稿を含むスレッドを表示] [この投稿を削除]
[552] Re:reallocについて
投稿者:774RR
2007/02/20 02:13:25

>矢印の箇所でgotoしたとき、prmはNULLだからSegmentation faultになりませんか。 C89 でどうだったかは知りませんが少なくとも C99 では free() の解説に、空ポインタの場合何もしないとあるので無問題です。 MSDN や HPUX11.00 の man にも同一の旨の記述があります。
[この投稿を含むスレッドを表示] [この投稿を削除]
[553] Re:reallocについて
投稿者:れぷ
2007/02/20 02:13:25

>free() の解説に、空ポインタの場合何もしないとあるので無問題です。 C89もセーフですよ。
[この投稿を含むスレッドを表示] [この投稿を削除]
[554] Re:reallocについて
投稿者:れぷ
2007/02/20 02:13:25

>realloc() に失敗する状況でどうリカバリーするかの仕様次第ですけど、 >書かない癖つけたほうがいいと思う。 私もそう思います。 なので私のサンプルはメモリの再配置が多そうだったことも含めて 完全にfree()してしまったりしてます(^-^;)
[この投稿を含むスレッドを表示] [この投稿を削除]
[555] Re:reallocについて
投稿者:本多
2007/02/20 02:13:25

>>>free() の解説に、空ポインタの場合何もしないとあるので無問題です。 >>C89もセーフですよ。 確かにfree(NULL)は無問題なのですが、以下の※の箇所は大問題でした。 | if ( (prm = malloc( sizeof( typChangePrm))) == NULL) { | fprintf(stderr,"ERROR: memory allocation\n"); | goto LERROR; ←ココ | } (中略) | LERROR: | free( prm->cpItem); ← ※ | free( prm); | return NULL; |} このような箇所は「if ( prm ) free( prm->cpItem);」が必要ですね。 以下の箇所も間違っていますね。 誤>void PrmDestroy( typChangePrm *prm) 誤>{ 誤> int i; 誤> 誤> for ( i=0; i<prm->itemCount; i++) 誤> free(prm->cpItem); 誤> free(prm->cpItem); 誤> free(prm); 誤>} 正>void PrmDestroy( typChangePrm *prm) 正>{ 正> int i; 正> 正> if ( prm && prm->cpItem) { 正> for ( i=0; i<prm->itemCount; i++) { 正> free(prm->cpItem[i]); 正> } 正> } 正> if ( prm) free(prm->cpItem); 正> free(prm); 正>} 大変失礼しました。私の能力の低さが露呈してます(T-T)
[この投稿を含むスレッドを表示] [この投稿を削除]
[556] Re:reallocについて
投稿者:774RR
2007/02/20 02:13:25

>>free() の解説に、空ポインタの場合何もしないとあるので無問題です。 >C89もセーフですよ。 とはいえいわゆる non-ANSI な超古い処理系ではダメなのもあったような記憶がうっすらと。 そーいう化石のようなプラットフォームまで考えるかどうか次第かな。
[この投稿を含むスレッドを表示] [この投稿を削除]
[557] Re:reallocについて
投稿者:れぷ
2007/02/20 02:13:25

>とはいえいわゆる non-ANSI な超古い処理系ではダメなのもあったような記憶がうっすらと。 あ、non-ANSIは知らないです。いわゆる「ぬるぽ」になるかもしれませんね。
[この投稿を含むスレッドを表示] [この投稿を削除]
[558] Re:reallocについて
投稿者:れぷ
2007/02/20 02:13:25

>大変失礼しました。私の能力の低さが露呈してます(T-T) ぐぁっ、私はそこって全然気がつきませんでした(T_T)
[この投稿を含むスレッドを表示] [この投稿を削除]
[564] Re:reallocについて
投稿者:SEC
2007/02/20 02:13:25

>正>void PrmDestroy( typChangePrm *prm) >正>{ >正> int i; >正> >正> if ( prm && prm->cpItem) { >正> for ( i=0; i<prm->itemCount; i++) { >正> free(prm->cpItem[i]); >正> } >正> } >正> if ( prm) free(prm->cpItem); >正> free(prm); >正>} 上記関数のfree(prm);の部分ですが、 この処理でprmはきちんと開放されているのでしょうか。 Cの引数は値渡しなので、(この場合、引数はポインタですが、ポインタも値渡し) 引数を変更しても呼び出し元には影響を与えないように思います。 free(prm->cpItem);のように、ポインタの中身に対する変更は問題ないと思います。
[この投稿を含むスレッドを表示] [この投稿を削除]
[565] Re:reallocについて
投稿者:774RR
2007/02/20 02:13:25

prm が指す先のメモリ領域は正しく開放されます。 その結果 prm 自体は無効なメモリ領域を指すようになるだけです。 まったく一切何の問題もありません。 >引数を変更しても呼び出し元には影響を与えないように思います。 そこのところはまさに御意なのですが、 どこで「prm そのもの」を操作しているのでしょうか? prm が指す先を操作しているだけです。 ごくふつーに、配列等で行っている処理とまったく同じ事をしていますよ。
[この投稿を含むスレッドを表示] [この投稿を削除]
[566] Re:reallocについて
投稿者:774RR
2007/02/20 02:13:25

あーなんか誤解を招きかねない?のでちょっと修正 malloc 等で得られたポインタ値が prm に入っているとします (仮に 0xabcd としよう) free(prm); する前の時点では prm==0xabcd であり 0xabcd 番地のメモリを使うことができます。 free(prm); した後の時点でも prm==0xabcd ですが 0xabcd 番地のメモリは使えなくなっています。 ってただそれだけの話ですね。 # 厳密に言えば 0xabcd 番地にメモリはあるが、使用権限が無いというべきか。 free(prm); の後に free(prm->cpItem); のような操作をしてはいけません。 free(prm); した時点で prm の指す先は無効となっています。 こういうのをダングリングポインタとか言いますな。
[この投稿を含むスレッドを表示] [この投稿を削除]