- 締切済み
トリプルポインタが必須!となる状況ってありますか?
不具合が生じ、業務ソースのエラー処理の部分まで掘り進んで見ていたら、 トリプルポインタ(ポインタのポインタのポインタ)を使用している部分がありました。 外注で中国産のソースのようで、当人に聞くことはできず、 周りの知識人に聞いてみたのですが「それでうまく動いているんならいいんじゃないの?」と 素っ気ない対応でした。 トリプルポインタが必須となる状況はあるのでしょうか? 構造体がネストしていてそのメンバに文字列があって… となっている場合などでもダブルポインタ(ポインタのポインタ)で処理できるように思います。 そんなときにトリプルポインタを使うよっていう状況やサンプルがありましたら教えてください。 よろしくお願いいたします。 ちなみに、不具合は単純なものですぐに解決しました。
- みんなの回答 (6)
- 専門家の回答
みんなの回答
- Tacosan
- ベストアンサー率23% (3656/15482)
C では void * とその他のポインタ型の間でキャストなしに値のやりとりをすることができます. で malloc のプロトタイプは void *malloc(size_t) ですから, もともと「返り値を int * (など) にキャストする必要性」はないんです. プログラミングスタイルとしてキャストしているだけ, でしょう. C++ では「void * への変換」はキャストなしで OK ですが「void * からの変換」には明示的なキャストを要求されます.
- Tacosan
- ベストアンサー率23% (3656/15482)
double の 3次元配列は array[x][y][z] でアクセスしたいし, そうすると array[x][y][z] が double → array[x][y] は double * → array[x] は double ** → array は double *** になるのが「自然」でしょ, ってこと. もちろん #4 のように array[(x*SIZEY + y)*SIZEZ + z] でアクセスしてもいいけど, あんまり自然って感じがしない. メモリ効率はいいんだけどねぇ. ちなみに個人的には int *p = (int *)malloc(sizeof(int) * n); よりも int *p = malloc(sizeof *p * n); の方が好き.
- kmee
- ベストアンサー率55% (1857/3366)
例えば 画像は二次元配列で実装すると便利です。 画素をPIXEL型とすると、変数imageに動的確保するには PIXEL ** image = (PIXEL **)malloc(sizeof(PIXEL *)*HEIGHT); /*(1)*/ for( y=0 ;y<HEIGHT;++y ){ image[y] = (PIXEL *)malloc(sizeof(PIXEL)*WIDTH); /*(2)*/ } とするのが常套手段の一つです。ここでPIXELへのポインタへのポインタが使われます。 横1列分のPIXELの配列を、高さ分用意します(2)。それを収めるのに、PIXEL *の配列を用意します(1)。 このimageを参照渡しして、別の画像に書き換えたり、解放してNULLを代入したりするような関数を作るなら、引数の型は PIXEL ***になります。 この画像を複数枚使いたければ、image1,image2...と変数を用意するのもよいですが、 PIXEL ** の配列として用意するのが普通です。PIXEL ** の配列は PIC *** と同等です。 そして、動的に確保するなら PIXEL *** images = (PIXEL ***)malloc(sizeof(PIXEL **)*NUM_OF_IMAGES); です。 特定の画素を利用する場合は images[画像番号][Y座標][X座標」 と三次元配列のようになります。 また、画像番号=Z座標と考えれば、立体と見做せます。 *が並んでややこしく思えますが typedef PIXEL * HLINE ; /* PIXELの列がHLINE */ typedef HLINE * IMAGE ; /* HLINEの列がIMAGE */ として LINE *image = (LINE *)malloc(sizeof(LINE)*HEIGHT); IMAGE *images = (IMAGE *)malloc(sizeof(IMAGE)*NUM_OF_IMAGES); とすれば、見慣れた形ではないでしょうか。 逆に typedef COLOR_INFO * PIXEL ; /* PIXELは色情報へのポインタ */ となっていたら、COLOR_INFOで表記すれば COLOR_INFO ****images = (COLOR_INFO ****)malloc(sizeof(COLOR_INFO ***)*NUM_OF_IMAGES); と*が1こずつ増えて「クアドラプルポインタ?」になります。 /* 余談: 実際には、画像は幅x高さの1次元配列として実装し、(X,Y)は[ Y * WIDHT + X ]でアクセスする、という場合も多いです。 */
お礼
ご回答ありがとうございます。 そろそろ家を出なければならず時間がないので書いていただいたソースをきちんと 読んで理解できていませんが、お礼を。 ポインタのポインタを扱う情報が複数あるので、それを個別にmallocしていてはメモリ上で飛び飛びの位置に領域が確保される可能性があるので喜ばしくはない、 なので最初から複数情報分まとめてmallocすれば、複数情報がメモリ上で連続した領域に確保され好ましい、かつ、配列としてアクセスできる、ということでしょうか。 仕事でソースを見ていて、 >image[y] = (PIXEL *)malloc(sizeof(PIXEL)*WIDTH); /*(2)*/ >PIXEL ** image = (PIXEL **)malloc(sizeof(PIXEL *)*HEIGHT); /*(1)*/ の例にだしていただいたように、mallocの前に*がひとつだったり**だったりするのが 混在していて、その意味を読み取れずに混乱しています。 ですので?、mallocで確保した構造体領域を開放するのに、間違ったFree()の使い方をしてメモリリークにしたりしてしまっています。。 市販のよくあるテキストではポインタの基礎概念と文字列(配列と等価)へのポインタ、参照渡しと値渡しの仕組みなどが説明されているものが多いのですが、 二次元配列(や三次元配列)、構造体への配列、鬼門のmallocとの関係を詳細に解説してあるテキストに巡りあえずにいます。 配列、値渡し、参照渡しはわかりますが、それが応用されるととたんに「?」です。。 やはり、地道に自分でコードを書いて勉強していくしかないのでしょうか。 一年以上Cで(下働きですが)業務していてまだこんなこともわからないのかと思うと悲しくなるものがあります。 「クアドラプルポインタ(なんてものも理屈上は可能ですね)」が出るようなソースになっても、それなりにスラスラと理解できる読解力を身につけたいです。 地道にテキストを購入して自作ソースで勉強させていただきます。 書籍については別途質問を立てましたので http://oshiete.goo.ne.jp/qa/6481380.html もしおすすめの書籍などがありましたらよろしくお願いいたします。 実例ソースを書いていただきまして誠にありがとうございました。
- Tacosan
- ベストアンサー率23% (3656/15482)
念のため補足: #2 の「C99 の~」は C99 の言語仕様上, そもそも「ポインタのポインタ」を使わないと決してプログラムが作れないということはない という意味です. ライブラリに「ポインタのポインタ」を要求する奴がいるのでその意味では言語仕様に必須ですが, そのような奴を除けば理論上「ポインタのポインタを使わなければ決してかけないプログラム」は存在しません. もちろん「ポインタのポインタを必要とする奴」も, 言語仕様を変更すれば「ポインタのポインタ」を必要としないようにできますが.
お礼
ご回答ありがとうございます。 3次元の構造を持つなら 3次元配列が必要であり, それを動的に確保しようとしたら「ポインタのポインタのポインタ」は自然な発想だと思う. というのは、私には難しすぎました。 実際にテストコードを書いて理解していこうと思います。 C99 の言語仕様上, そもそも「ポインタのポインタ」を使わないと決してプログラムが作れないということはない というのは、なんとなくですが感覚でそんな気がします。 まだまだ理解が浅いです。 頑張って勉強したいと思います。 もしよろしければおすすめの書籍を教えてください。
補足
回答者1-3様 土日は所要のためネットに繋ぐことができません。 再度ご回答を頂いた場合、お返事が遅れることをご了承ください。
- Tacosan
- ベストアンサー率23% (3656/15482)
3次元の構造を持つなら 3次元配列が必要であり, それを動的に確保しようとしたら「ポインタのポインタのポインタ」は自然な発想だと思う. というのは現実的な話. C99 の言語仕様上は「そもそも『ポインタのポインタ』ですら必須ではない」とも言える.
- kmee
- ベストアンサー率55% (1857/3366)
「『ポインタへのポインタ』へのポインタ」が必要なとき 例えば 「ポインタへのポインタ」の参照渡し 「ポインタへのポインタ」の配列 「ポインタへのポインタ」のmalloc等による動的確保 無理矢理「意味付け」をしようとしていませんか? いくつ*/&が付こうが、ポインタはポインタです。 >構造体がネストしていてそのメンバに文字列があって… >となっている場合などでもダブルポインタ(ポインタのポインタ)で処理できるように思います。 この方が意味不明です。 struct A{ struct A * a; char * s; } だとしても、 A.a->s は「構造体Aへのポインタa→その実体(構造体A)→そのメンバs(charへのポインタ)」であって「ポインタへのポインタ」にはなってないですし。
お礼
ご回答ありがとうございます。 私のポインタに対する理解が浅いんですね。。 ポインタ完全制覇とか、秘伝問答ポインタ編などは読了したのですが、 基礎的な仕組みがしっかり理解出来ていないから応用的なことも理解出来ていないのかと思いました。 ウチのチームが持っている開発部分は全体のごく一部で、外部から要求される仕様に対して、職場の諸先輩方は「*とか&つけて試行錯誤して動かしたい通りに動けばそれが正しい」というスタンスで、派遣社員の方がポインタについてかなり詳しいのです。 が、その派遣の方が切られてしまい、ポインタに対するチーム全体の理解がかなり浅いのが実情です。 ご指摘の、 「ポインタへのポインタ」の参照渡し、は関数に渡すときと理解しました。 「ポインタへのポインタ」の配列は、そういうものが必要になるときがあるのか…という印象です。 「ポインタへのポインタ」のmalloc等による動的確保については今の私ではちょっと理解できませんでした。 まだ理解が足りませんが頑張ってみたいと思います。 また、もしよろしければ何かおすすめの書籍を教えてください。
お礼
ご回答ありがとうございます。 三次元配列の例は具体的でわかりやすかったです。 やっぱりメモリ上でどう確保されて、どうアクセスするかがイメージ出来ていないといけないんだなと思いました。 またうちの会社では int *p = (int *)malloc(sizeof(int) * n); 一本ですね。もうひとつの書き方は初めて見ました。この書き方だとint*型にキャストする必要がないんですね。 まだまだ勉強しないといけないことがたくさんあると感じるご回答でした。 これからも勉強していこうと思います。 また、お礼が遅れまして失礼しました。