- ベストアンサー
ポインタお手上げです。
#include <stdio.h> main() { void original(int[], int *[], int *[], int *[]) ; int i, origin[10]; int m2[10],m3[10],m5[10]; for(i = 0; i < 10; i++){ scanf("%d", &origin[i]); } original(origin, &m2[], &m3[], &m5[]); printf("2の倍数:"); for(i = 0; i < 10; i++){ printf("%2d ,", m2[i]); } printf("\n"); printf("3の倍数:"); for(i = 0; i < 10; i++){ printf("%2d ,", m3[i]); } printf("\n"); printf("5の倍数:"); for(i = 0; i < 10; i++){ printf("%2d ,", m5[i]); } printf("\n"); return 0; } void original(int origin[], int *multiple_2[], int *multiple_3[], int *multiple_5[]) { int i, m2 = 0, m3 = 0, m5 = 0; for(i = 0; i < 10; i++){ if(origin[i] % 2 == 0){ *multiple_2[m2] = origin[i]; m2++; } if(origin[i] % 3 == 0){ *multiple_3[m3] = origin[i]; m3++; } if(origin[i] % 5 == 0){ *multiple_5[m5] = origin[i]; m5++; } } } 上のプログラムをコンパイルし実行するとセグメントエラーがでてしまいます。どこがわるいんでしょうか・・・。上のプログラムは、外部入力から10個の数字をoriginに格納しそれをそれぞれ2の倍数、3の倍数、5の倍数をあらわすポインタにそれぞれを格納していき、最後にそれを表示するプログラムなんですが・・・。いまいち配列を持ったポインタを使い、なおかつ引数として使うやり方がうまくわからないからこんなエラーが出るような気もします・・・。どなたか初心者でもわかるようなアドバイスお願いします!
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
No.3の者です。 ポインタはアドレスなので、 int origin[10], *op=origin; のところで、intが4バイトの場合、 例えば origin[10]がメモリのアドレスFFA0~FFC8までにとられ、 opはポインタ変数で、origin(*origin[0])のアドレスFFA0が入ります。 (配列名originは、その配列の最初のポインタ) >m2p = m2; m3p = m3, m5p = m5; >↑の行は何故必要なんでしょうか。実際この行がなしでコンパイル >したんですけど上手くいきません。どうしてでしょうか・・・。 ループの中の、 *m2p++ = *m3p++ = *m5p++ = 0; の行で、0を入力していくために、それぞれのポインタをインクリメントしています。 (ループ終了時には、例えばm2pは、存在しないm2[10]のアドレスを示しています) 関数に渡す際には、m2[0]から埋めていってもらうために、 また、倍数を表示させる際は、またm2[0]から順に見ていく必要があるので、 一度m2pにm2を代入し、m2pをm2[0]のアドレスに戻さなくてはいけません。
その他の回答 (4)
- a0123456789
- ベストアンサー率22% (57/255)
No.2の者です。 じっくりプログラムを読まずに回答してしてしまい、ポイントがズレてしまっていたみたいですね。すみません。 ところで、じっくりとあなたが書かれたプログラムを見ますと理解に苦しむところが1点あります。 (1)関数originalの引数。 第1引数のみ整数の配列(int[])で、後が整数の配列のポインタ(int *[]) となっている点。 どうして第1引数のみが、整数の配列なのでしょうか?全て整数の配列とした方が判りやすいしよいのでは? あなたが書かれたプログラム中の矛盾点(BUG)は下記の点では? (1)関数originalの第2仮引数以降の使用方法 第2仮引数以降は整数の配列のポインタとして宣言されているので、下記のようにするべきでは、 *multiple_2[m2] → multiple_2[m2] あなたが書かれたプログラムでは、 *multiple_2[m2] →*(multiple_2[m2])となり、 配列multiple_2からm2番目の配列(配列multiple_2のm2番目の要素ではありません)の第1要素を示すことになり、これは多分不定になっているので不正なところに書き込みをすることとなりセグメントエラーがでてしまているのではないかと思われます。
お礼
回答ありがとうございます。 確かに、全て配列としたほうがわかりやすかったですね・・・。別のプログラミングを参考にしながらやっているとこのようになってしまいました。 二番目のご指摘につきましては、確かにポインタの扱い方がおかしいですね。言われて読み返してみると間違いに気付きました。どうもご指摘ありがとうございました。
- panda0000
- ベストアンサー率35% (59/165)
紛らわしくしてしまうかもしれないので申し訳ないのですが、 せっかくポインタの勉強をするのでしたら、配列の[]を使わないで組む練習をしてみるのもいいかと思います。 少し書き換えてみましたので(短時間なので汚いですが)、 もしよろしければ読み解いていただくと勉強になるかと思います。 int main() { void original(int *, int *, int *, int *) ; int i, origin[10], *op=origin; int m2[10], m3[10], m5[10], *m2p=m2, *m3p=m3, *m5p=m5; for(i=0; i<10;i++) { scanf(" %d", op++); *m2p++ = *m3p++ = *m5p++ = 0; } m2p = m2; m3p = m3, m5p = m5; original(origin, m2, m3, m5); printf("2の倍数:"); while (*m2p) { printf("%2d ,", *m2p++); } printf("\n"); printf("3の倍数:"); while (*m3p) { printf("%2d ,", *m3p++); } printf("\n"); printf("5の倍数:"); while (*m5p) { printf("%2d ,", *m5p++); } printf("\n"); return 0; } void original(int *origin, int *multiple_2, int *multiple_3, int *multiple_5) { int i, m2 = 0, m3 = 0, m5 = 0; for (i = 0; i < 10; i++, origin++) { if (*origin % 2 == 0){ *multiple_2++ = *origin; } if(*origin % 3 == 0){ *multiple_3++ = *origin; } if(*origin % 5 == 0){ *multiple_5 = *origin; } } }
お礼
返信ありがとうございます。 配列を使わずにやるとすごいコンパクトになりますね。大変勉強になりました。 すこしわからないとこがあるんですが m2p = m2; m3p = m3, m5p = m5; ↑の行は何故必要なんでしょうか。実際この行がなしでコンパイルしたんですけど上手くいきません。どうしてでしょうか・・・。
- a0123456789
- ベストアンサー率22% (57/255)
配列名は確かポインタ定数のはずですので下記のところが 間違っているのでは? 誤 original(origin, &m2[], &m3[], &m5[]); 正 original(origin, m2[], m3[], m5[]); 後、うろ覚え(幾分忘却の彼方)なので、確認願いますが、 *と[]の優先順位はどうでしたか? *multiple[] は、(*multiple)[] or *(multiple[]) のどちらでしたっけ? (前者は配列のポインタ、後者は配列のポインタのポインタだったようナ!)
お礼
返信ありがとうございます。 正のほうに変えてみたところやはりエラーが出てしまいます。 優先順位は・・・おそらく[]のほうがさきだったような・・・
- nitscape
- ベストアンサー率30% (275/909)
ポインタ以外は理解できているようですね。 「ポインタ」という言葉に惑わされず、単なる「アドレス(メモリ上の位置を示す数字)」ということを頭に常に置くと理解しやすいと思います。ある関数にアドレスを渡せば、その関数内ではそのアドレスに対して読み書きすることができます。そうするとそのアドレスに書き込まれた値はプログラム上のどの部分から読み出すことができる...という感じです。 TRACEはprintfに直してください。またscanfも消したのでご自分で入れてください。 #include <stdio.h> void original(const int* origin,int* multiple_2,int* multiple_3,int* multiple_5) { int i, m2 = 0, m3 = 0, m5 = 0; for(i = 0; i < 10; i++){ if(origin[i] % 2 == 0) { multiple_2[m2] = origin[i]; m2++; } if(origin[i] % 3 == 0){ multiple_3[m3] = origin[i]; m3++; } if(origin[i] % 5 == 0){ multiple_5[m5] = origin[i]; m5++; } } } main() { int i, origin[10]; int m2[10],m3[10],m5[10]; for(i = 0; i < 10; i++) { origin[i] = i; m2[i] = 0; m3[i] = 0; m5[i] = 0; } original((int*)origin,(int*)m2,(int*)m3,(int*)m5); printf("2の倍数:"); for(i = 0; i < 10; i++){ TRACE("%2d ,", m2[i]); } TRACE("\n"); printf("3の倍数:"); for(i = 0; i < 10; i++){ TRACE("%2d ,", m3[i]); } TRACE("\n"); printf("5の倍数:"); for(i = 0; i < 10; i++){ TRACE("%2d ,", m5[i]); } TRACE("\n"); return 0; }
お礼
返信ありがとうございます。 とてもよくわかりました。 ただ一つこの行がいまいち理解できないのですが・・・ original((int*)origin,(int*)m2,(int*)m3,(int*)m5); 特に (int*)の部分がわからないのですが・・・。
お礼
なるほど!!大変よく分かりました!!ということは、 *m2p++ = *m3p++ = *m5p++ = 0; の行を *(m2p++) = *(m3p++) = *(m5p++) = 0;としてforループを一回分減らせば、先頭のアドレスに変更が無いので m2p = m2; m3p = m3, m5p = m5; は、記述しなくてもいいってことにもなりますよね? 二度も回答してくださってほんとうにありがとうございました!!