- ベストアンサー
ポインタのポインタの関数受け渡しについて
- ポインタのポインタを利用したプログラムを作成しています。
- 配列のセットにはset関数を、表示に関する処理をpt関数で行いたいと思っています。
- dt、set関数、pt関数の引数にはどのような値を入れればよいか理解できません。
- みんなの回答 (8)
- 専門家の回答
質問者が選んだベストアンサー
こんにちは。もう、解決しました? ポインタ・・・悩ましいですよね。ポインタのポインタ・・・配列、構造体。でもって、それを参照渡しとか、もう、変数の実体はどこ~?って感じです。どやって値を書くの、読むのと書き成れた人でも、試行錯誤でデバグするのではないでしょうか。 必ずしも、やまさんの疑問と一致しているとはいえないですが、次のことに注目してサンプルをくみ上げてみました。 (1) main()で、int形2次配列で出来たデータテーブルの位置を示すポインタdtを用意する。 (2) set()でデータテーブルの領域を確保する。確保した領域は、参照渡し引数dtに返す。ついでにデータテーブルにも値を入れてしまいます。 (3) ptでデータテーブルを表示する。 ---------- サンプルソース ここから ----- (注意) 整形のため全角スペースを使ってます。 (注意) 勝手に3x3の配列にしてます。 01: #include<stdio.h> 02: /* FUNCTION PROTOTYPE */ 03: void set(int***); 04: void pt(int**); 05: /* MAIN */ 06: int main(void){ 07: int **dt; 08: set(&dt); 09: pt(dt); 10: } 11: /* DT MEMORY ALLOCATE AND SET SOME VALUE */ 12: void set(int ***dt){ 13: int i,j,k; 14: k=3; 15: *dt=(int**)malloc(sizeof(int*) * k); 16: for(i=0;i<k;i++){ 17: (*dt)[i]=(int*)malloc(sizeof(int*) * k); 18: } 19: for(i=0;i<k;i++){ 20: for(j=0;j<k;j++){ 21: (*dt)[i][j]=i*k+j; 22: printf("set (*dt)[%d][%d]=%d\n", i, j, (*dt)[i][j]); 23: }} 24: } 25: /* DISPLAY DT */ 26: void pt(int **dt){ 27: int i,j,k; 28: k=3; 29: for(i=0;i<k;i++){ 30: for(j=0;j<k;j++){ 31: printf("%d ", dt[i][j]); 32: } 33: printf("\n"); 34: } 35: } ---------- サンプルソース ここまで ----- ちょびっと説明:ヒントになれば幸いです。 (1) setの引数dtのポインタの深さについて 5行目、12行目を見てください。確保したメモリの領域を返さなければなりませんので、2次元配列ポインタ**dtを示すアドレスを渡す為、***dtとしなければなりません。そして、mainからは、dtのアドレス&dtを渡さなければなりません。参照わたしですね。 (2)領域確保 main内のdtとset内の(*dt)は等価、同じ意味です。 一次元目の領域確保は、(*dt)に対しておこないます。2次元目の領域確保は、それぞれ(*dt)[i]に対しておこないます。僕の文力で説明できないです(大汗)。 (3) ポインタに書き込む、読み出す 21行目、22行目を見てください。(*dt)[i][j]です。ポインタを示す*印は、演算子というのですが、順位的に弱いことの多い演算子です。順位というのは、たし算とかけ算は、掛け算が先というような、解釈の順番です。配列や構造体が絡んできた時は、負けちゃいますので、いつも括弧でくくって守ってやっています。 どですか? Magna
その他の回答 (7)
- Tacosan
- ベストアンサー率23% (3656/15482)
余談ですが, sizeof には sizeof (型) sizeof オブジェクト の 2つの形式があります. で, malloc で使うなら (安全のためにかっこを余計に付けるけど) x = malloc(sizeof (*(x)) * n); のように, 後者の形で使う方が安全だと思います. 今の例だと *dt = malloc(sizeof (*(*dt)) * k); とする. 左辺の *dt と sizeof の中の *dt を一致させれば, 常に正しいサイズで確保できます.
- Oh-Orange
- ベストアンサー率63% (854/1345)
★『char **alloc』を宣言しない場合 >一つ初歩的な質問なのですが、set関数の中で**allocを宣言していますが、 >しない場合はどのように記述するべきなのでしょうか。 >*dt = (int**)… > >*dt[i] = (int*)… >のようにしてもだめですよね。 ↑ (*dt) = (int**)malloc( sizeof(int*) * k ); (*dt)[ i ] = (int*)malloc( sizeof(int) * k ); となります。 1つ目の dt にはカッコは必要ないです。 2つ目の dt にはカッコを付けないと意味が変わってきます。 ※安全策として malloc で確保できなかった場合の処理も書いておきましょう。 >二つ目のmallocの件、ありがとうございます。 >見落としていました。 ↑ 私も見落としてしまいました。 ・過去質問より次のリンクを紹介しておきます。 動的に二次元配列を確保する場合の方法です。 http://oshiete1.goo.ne.jp/qa3022605.html→『callocで二次元配列を作成するには?』 ↑ この方法なら malloc、free の回数が減るためエラーチェック、解放なども早くなります。 最後に int ** で使える二次元配列の確保と解放関数を紹介しておきます。 サンプル: #include <stdio.h> #include <stdlib.h> // n×nの二次元配列を確保 int **alloc_matrix( int n ) { int **aa; int *a; int i; if ( (aa = (int **)calloc(n + 1,sizeof(int*))) != NULL ){ if ( (a = (int *)calloc(n * n,sizeof(int))) != NULL ){ for ( i = 0 ; i < n ; i++ ){ aa[ i ] = &a[ i * n ]; } aa[ n ] = NULL; // ちょっとした工夫 return aa; // 正常 } free( aa ); } return NULL; // エラー } // n×nの二次元配列を解放 int **free_matrix( int **a ) { if ( a != NULL ){ free( a[0] ); free( a ); } return NULL; } // 使い方 int main( void ) { int **p; // 10×10の二次元配列を確保 if ( (p = alloc_matrix(10)) != NULL ){ : (処理) : p = free_matrix( p ); } return 0; } 以上。良かったら今後、活用してみて下さい。
- Tacosan
- ベストアンサー率23% (3656/15482)
う~ん, どうやら全然理解してもらえなかったらしい.... main の dt と set の dt は全く別物なんです. だから, この状態ではせっかく set の中で malloc しても, その領域をプログラムの他のところで使う方法が存在しないんです. 例えば, #include <stdio.h> void set(int x) { x = 5; } int main() { int x = 0; set(x); printf("x = %d\n", x); return 0; } としても, 「x = 0」としか表示されません. でどうするかというと void set(int *x) { *x = 5; } とポインタを使って定義して, main の方も set(&x); と呼び出す. これは理解できてますか? これが理解できていれば, あなたのプログラムにおいて main で set(dt); と呼び出しているのがおかしいことに気付くはずです. ちなみに set の中の 2つ目の malloc は sizeof(int*) じゃなくて sizeof(int) ね.
- Oh-Orange
- ベストアンサー率63% (854/1345)
★set()関数はポインタのポインタではないですね。 ・良くソースを見たらポインタのポインタに二次配列を確保しようとしていますね。 そしてそのポインタをset()、pt()に渡すのであれば『ポインタへのポインタ』のポインタを 渡します。つまり set( int ***dt ) となります。pt()関数は pt(int **dt)だけで良いです。 // 手直し版 int main( void ) { int **dt; set( &dt ); ←set()関数は『&』演算子が必要です。 pt( dt ); ←pt()関数はそのままで良い。 } // 引数宣言を見よ(1つ多くすること) void set(int ***dt ) { char **alloc; ←これを宣言した方が分かりやすいでしょう。 alloc = (int**)malloc( sizeof(int*) * k ); for ( i = 0 ; i < k ; i++ ){ alloc[ i ] = (int*)malloc( sizeof(int*) * k ); } *dt = alloc; ←注目 } これで問題は解決できますか? ちなみに過去質問に二次配列をmallocする方法が多数あります。 時間を掛けて検索すればもっと良いアドバイスが見つかります。 私も過去にいくつか回答を書いていますので…。
お礼
なるほど・・ありがとうございます。とても勉強になります。 ポインタへのポインタ のポインタというのに全く気がつきませんでした。 ただ、私のソースはあまりよいソースではないようですね・・難しいですね。 一つ初歩的な質問なのですが、set関数の中で**allocを宣言していますが、 しない場合はどのように記述するべきなのでしょうか。 *dt = (int**)~~~ *dt[i] = (int*)~~ のようにしてもだめですよね。
- Tacosan
- ベストアンサー率23% (3656/15482)
その x って関数はなんでしょうか? 変更したあとのプログラムを, 可能な限り出してもらえるとうれしいんだけど. ちなみに set の中の malloc でおかしいところがありますね.
お礼
void set(int **dt); void pt(int **dt); int main(void){ int **dt; set(dt); pt(dt); } void set(int **dt){ dt = (int**)malloc(sizeof(int*) * k); for(i = 0; i < k; i++){ dt[i] = (int*)malloc(sizeof(int*) * k); } のように配列サイズの動的確保が目的 } void pt(int **dt){ 二重forループ{ printf(dt[i][j]); } } のように変更を加えた結果、コンパイル時のエラー、警告はなくなりました。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★サンプル #include <stdio.h> // 関数 void sample( int **pos ) { *pos += 5; } // メイン int main( void ) { int array[ 10 ] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int *p0 = (array + 0); int *p5 = (array + 5); sample( &p0 ); if ( p0 == p5 ){ printf( "一致\n" ); } else{ printf( "不一致\n" ); } return 0; } 上記のサンプルを活用してみて下さい。 sample()関数はポインタのポインタを受け取っています。 以上。
お礼
サンプルまで用意していただきありがとうございます。 参考になります。 しかし、これを踏まえ色々なパターンを試してみましたがやはり実行できません。
- Tacosan
- ベストアンサー率23% (3656/15482)
set に渡すところだけやるけど, 「ポインタのポインタ」と難しく考えるからいけないんです. int a; に対してこの a の値を別の関数 set で変えようとしたら set(&a); と渡すし, 受ける方は void set(int *a) などとなりますよね. これと同じです. 一般論として, T という型のデータを変えようとしたら, 受ける方では型 T* を持つ型が必要です. T = int なら T* = int * だし, T = int * なら T* = int **.
お礼
ありがとうございます。 void x(int **dt){} x(int *dt); としてみました。 結果エラーはなくなりましたが、実行段階でエラーが出ます。 表示の部分の関数間の受け渡しが上手くいってないのかもしれません。
お礼
何度もありがとうございます。&の意味がようやく分かりました。 難しく難しく考えてしまって基本的なことを忘れていたようです。 二つ目のmallocの件、ありがとうございます。 見落としていました。