- ベストアンサー
多次元配列の引渡しについて
メイン関数と 副プログラム(?)間での 三次元配列の数値の引渡しは可能なのでしょうか? 今の私のプログラムだと どうもうまく引き渡されていないようなのです。初心者のため いきづまってしまいました。どなたか分かる方 教えていただけると助かります。
- みんなの回答 (8)
- 専門家の回答
質問者が選んだベストアンサー
>行列aの全ての要素に1を足して返させる //別のやり方のサンプルを作ってみました。 #include<stdio.h> #define n 2 void add1ToA(double *d, double*s, int size){ //先頭のアドレスと配列のサイズで処理 int i; for(i=0;i<size;i++) *d++ =*s++ + 1.0; } void printA(double a[n][n]){ int i, j; for(i=0;i<n;i++){ for(j=0;j<n;j++){ printf("%f ",a[i][j]); } printf("\n"); } } int main(void){ double a[n][n],c[n][n]; int i,j; for(i=0;i<n;i++) for(j=0;j<n;j++) a[i][j]=0; //初期化 add1ToA(&c[0][0], &a[0][0], sizeof(a)/sizeof(double));// c ← a + 1 : a は、変更しない printA(a); printf("\n"); printA(c); return 0; }
その他の回答 (7)
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
#7補>sizeof(doubleの配列名)/sizeof(double)というのは どのような意味 sizeofは演算子で、オブジェクトのサイズを返します。 オブジェクトとは変数や構造体や型や構造体の型とかです。 doubleが8バイトで表されるとしたら sizeof(double) は、8になります。 double a[10]; の時 sizeof(a) は、80バイトになります。 上記の時 なので、sizeof(a)/sizeof(double)は、80/8=10 となって 配列のサイズを求めていることになります。 こうした手法は、 a[]={1,2,3,4}; とか配列のサイズを明記しないで初期化による自動的にサイズを決めた様な場合に、サイズを直接記した場合と違ってサイズを計算で求めているので、変更に際して意識しなくていいことがメリットです。 実際に、sizeofの結果を表示するようなプログラムを作って試してみるといいでしょう
お礼
今回 何度もご親切な回答 本当にありがとうございました! とりあえず今回質問した内容については 理解することが出来たかと 思いますが、まだ初心者なため 一つ分かったかと思うと 又別の 壁にぶち当たるということがたびたびのため、またいずれ 一人では 解決できないことが出来たときは こちらで質問させていただく かもしれません(^^ゞ 今回は本当にたびたびありがとうございました!
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
>サイズというものを利用するのですね! う~ん、う~ん(-~-); double *p; のような並びとして処理するからサイズが必要になるのです。 #5でもおっしゃっているように a[x][y] の様な場合、ポイントする位置を計算するには a[][y]のようにx は無くても(?)いいですが、 [x][y]の様にアクセスするには y は必要です。 つまり折り返す場所がわからんということですね。 a[x][y]とすればそれぞれの場所は a[x][y]でアクセスできますが、 a[x][y]を*pで受けるということは、 a[x*y]で受けるようなものです。 全体を1つの並びとして同じ処理をするなら、先頭のアドレスと、全体のサイズがわかれば処理できます。 そういうことです。 ケースバイケースで使うといいと思います。 >三次元でも、同じようにやればよいのでしょうか? 同じようなケースの場合同じようにできます。 >もっと複雑な計算も、上記の書き方を、応用すればよいのでしょうか? そうですね、どのようなケースでどの書き方が使えるかは一概には言えないように思いますので明言はできませんが #1の様に配列として扱う #4の様に[ ]を使わないように扱う あるいはその混用というようにプログラマのやりたいようにできます。 要は、今扱っているポイント先が何を表しているのかちゃんと把握していればいいのです。
お礼
(訂正です!) size じゃなくて sizeof でしたね(^^ゞ
補足
とても 分かりやすかったです!!ありがとうございます! *p++ という使い方と意味を 今回ここで教えていただいて 初めて知りました! ちなみに size(配列名)/size(double)というのは どのような意味を もつのでしょうか? size(配列名) とだけ記述するのとは何がちがってくるのでしょう?
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
>> 配列名は、ポインタではなくアドレス定数です。 >要するに「ポインタ」ですよね。。。 要するに「ポインタ」と言ってしまっては語弊があるので、 「アドレス定数」と言っております。 ポインタ演算ができることとポインタとは異なると思っております。 例えば 配列名を使って*(array + 5)のようなことはできても ポインタは++でポイントする位置をインクリメントできますが、当然配列名ではできません。 まあ、何を中心にして言っているかで違うのだということだと思いますが。
- ency
- ベストアンサー率39% (93/238)
配列=ポインタという理解は間違いです。 配列はコンパイラによって「先頭要素を指すポインタ」に読み替えられるんです。 つまり、 int hoge[30]; と定義した場合、メモリ上には int型 30個分の領域が確保されることになりますが、式の中で「hoge」と使った場合には、&hoge[0] と等価、つまり配列 hoge の先頭要素 hoge[0] を指すポインタとして扱われるわけです。 # ですので、BLUEPIXYさんの No2 での以下の回答は、はずしていると思うん # ですが…(??) # # > 配列名は、ポインタではなくアドレス定数です。 # # もっと細かいことを言えば、配列名は左辺値として使用できない # (値を代入できない) とか、通常のポインタが間接参照なのに対して # 配列名は直接参照であるとか、いろいろあるんで面倒くさいんですけど、 # 要するに「ポインタ」ですよね。。。 int hoge[30]; ⇒ hoge は &hoge[0] に読み替えられる。 このとき読み替えられる型は int を指すポインタ (int *型) という基本がわかっていれば、あとは同じことです。 int piyo[10][20][30]; ⇒ piyo は &piyo[0][0][0] に読み替えられる。 このとき読み替えられる型は「『intの配列(要素数30)』の配列(要素数20)」を指すポインタ (int (*)[20][30]型) となります。 # どうしてこのようになるのか、ご自分でじっくりと考えてみてください。 ちなみに、「配列」→「ポインタ」の読替えは再帰的には行われません。 ですので、「配列の配列」→「配列を指すポインタ」にはなりますが、そこからさらに「ポインタを指すポインタ」にはなりません。 さて、前置きが少々長くなりましたが、関数の引数の配列を渡す場合には、必ずポインタに読み替えられた形で渡ることになります。 int hoge[30]; を関数 func1 の引数として渡したい場合、たとえば 「func1( hoge );」のようにコールしたい場合には、 void func1( int *p_hoge ); という型の関数を用意する必要があります。 同様に int piyo[10][20][30]; を関数 func2 に渡したい場合、たとえば「func2( piyo )」のようにコールしたい場合には、 void func2( int (*p_piyo)[20][30] ); という型の関数を用意してあげる必要があります。 なお、関数の仮引数に限り、ポインタ表記と配列表記は同じ意味を持ちます。 # 正直、こんな面倒な仕様があるために、配列とポインタに関する誤解が広まっている # 気がしなくもないんですけどね。。。 つまり、上記 func1() と func2() は以下のように書いてもまったく同じ意味になります。 〔func1() の定義〕 (1) void func1( int hoge_array[] ); (2) void func1( int hoge_array[30] ); /* (2) の場合、配列要素数 30 は無視されます */ 〔func2() の定義〕 (1) void func2( int piyo_array[][20][30] ); (2) void func2( int piyo_array[10][20][30] ); /* (2) の場合、配列要素数 10 は無視されます */ 通常は、配列の定義・宣言いずれもポインタの定義・宣言と異なりますので、ご注意ください。 # int hoge[30]; # # と # # int *hoge; # # が違うことは、改めて申し上げる必要もありませんよね? 細かい処理の話は、BLUEPIXYさんにお任せします。 # あ…肝心なところ逃げた (?) とりあえず、ご参考まで。。。
補足
とてもご親切な回答なのに 読んで分かりそうで分からず 混乱してくる自分が情けないです(苦笑)しかし ありがとうございます! ええと、BLUEPIXYさんの書いてくださった通りにやってみたら ついに 値がきちんと 引き渡されたように見えたのですが、 三次元の配列を引き渡したい場合、 int piyo[10][20][30] は、func(&piyo[0][0][0],sizeof(piyo)/size(double))でコールしたいとする時、 void func(int *ppiyo, int size)-(1) ではダメなのですか? それと、void func2( int (*p_piyo)[20][30] ); と書くのと (1)式 との違いはどのようなことなのでしょう?また、(*p_piyo)[20][30] とは、どんな意味をもち、プログラム中で、数値を代入させたりする 計算式中では どのように用いればよいのでしょう? 通常通り for(i=0;i<n;i++){ for(j=0;j<n;j++){ for(k=0;k<n;k++){ (*p_piyo)[20][30]=1; } } }などとつかうのでしょうか?違いますよね(^^ゞ 質問攻めですみません・・・
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
#2礼>できれば三次元の配列に収めた数値を 三次元のまま 引き渡せるといいのですが・・・w それができることは、すでに#1で示したつもりです。 #2補> 補足されたプログラムのやりたいことは何でしょうか? よろしければ、やりたいことについて補足して下さい。 間違ったプログラムだけ示されても、意図が正しく伝わりません。 配列aを初期化したものをfuncで全ての要素について+1するというような意味なのでしょうか (もしそうなら、全然違います) for(i=0;i<n;i++){ for(j=0;j<n;j++){ b[i][j]=a[i][j]+1; return b[i][j]; } } では、ちょっと考えてみればわかるように、 i:0,j:0でリターンしてしまいますから いつでもa[i][j]+1の値が返されるだけです。 また、 return b[i][j]; で返されているのは、b配列の中の1つの要素の値であって、 b配列そのものではありません。 もし、a配列を処理した結果をc配列に入れたいというのなら aとcをfuncに渡して、そこで処理すれば良いです。 #1でも示したように、#1の様に配列を渡した場合、 受けた配列の処理はそのまま、その配列をいじることになります。
お礼
そうですね、すみません、私の記したプログラムは 行列aの全ての要素に1を足して返させる と試したかったのですが・・・ そうですか!このようなreturnの仕方は iとjの全ての組み合わせに対して値を返してくれるのだと思ってやってみてしまったのですが これだと i=j=0 の値しか返さず まったくの間違いなのですね! どうもありがとうございます!行列の全ての成分を同じ値に設定したため 間違いに気づきませんでした・・・。 aとcをfuncの引数とさせればいいのですね。試してみます!
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
>配列というのはもうポインタ変数になっている 配列名は、ポインタではなくアドレス定数です。 >ポインタとしての引渡しは一次元でしかできないのでしょうか?? もちろん、2次元とかでもできますよ。 一次元のポインタとして受ける場合は、 *pointerでいいと思います。 その場合(2次元のポインタを1次元で受ける場合)は、ポインタ計算に注意してやらないといけません。 2次元のポインタとして受ける場合は、 (*pointer)[size] 見たいな感じでいいと思います。 サンプルで配列として書いたのは、わかりやすさのためです 副プログラムでの変更が主プログラムに及んでいることから、 コピーされた配列というわけではないのがわかると思います。 難しいポインタの宣言をするときは、 typedefで適当に型を作って一段ずつやってやるとわかりやすくなります。 >tolower は、ctype.h で定義されている標準関数です。
お礼
BLUEPIXYさんの回答 とても詳しくて ありがたいのですが 私 実はまだかなり初心者で ポインタについても 本当に分かっているか否か 疑わしいレベルです(^^; できれば三次元の配列に収めた数値を 三次元のまま 引き渡せると いいのですが・・・w
補足
お返事遅くなりすみません! 三次元の配列を引き渡す際、例えば、以下のようなプロムで配列bを 引き渡したい場合、 #include<stdio.h> #define n 2 double func(a[n][n]){ int i,j; double b[n][n]; for(i=0;i<n;i++){ for(j=0;j<n;j++){ b[i][j]=a[i][j]+1; return b[i][j]; } } } main() { int i,j; double a[n][n],c[n][n]; for(i=0;i<n;i++){ for(j=0;j<n;j++){ a[i][j]=0; c[i][j]=func(a); } } return 0; } というように 副プログラムのreturn の後ろにおくことは可能なのでしょうか?実際上の簡単なプログラムを作って実行してみたところ 確かな実行結果が得られたので 本番のプログラムでも上のようにして 引き渡してみたのですが 引き渡す前はきちんとした値を持っていることを確認できたにも関わらず、引き渡した後の中身を見てみると 全て ゼロになっており、うまく引き渡されていないようなのです。 また、return の後ろにb[i][j][k]のような多次元配列をおいて それをさらにforループの中に置く というような例を見たことがないため このような使い方を本当にしていいものか 疑問もあります。結局そこのところを いまだにどうしたらいいのか困っている次第なのです(汗)
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
//どんな風に書いているのかわからないんですが、 //どんな風にやってるんですかね? //とりあえずのサンプル #include <stdio.h> #include <ctype.h> void tolowerD3(char data[3][3][3]){ int x, y, z; for(x=0;x<3;x++) for(y=0;y<3;y++) for(z=0;z<3;z++) data[x][y][z] = tolower(data[x][y][z]); } void main(void){ char dim3[3][3][3]={ {{'a','b','c'},{'A','B','C'},{'C','B','A'}}, {{'x','y','z'},{'X','Y','Z'},{'Z','Y','X'}}, {{'L','m','n'},{'N','m','L'},{'M','n','L'}}, }; int x, y, z; tolowerD3(dim3); for(x=0;x<3;x++){ printf("{"); for(y=0;y<3;y++){ printf("{"); for(z=0;z<3;z++){ printf("%c,", dim3[x][y][z]); } printf("},"); } printf("},\n"); } }
補足
お返事どうもありがとうございます!私は 数学の行列の計算を C言語で行っており 要は メイン関数のほうで数を 定義した行列を、 副プログラムのほうへも 引き渡したいのですが、 どうもうまくいってないようなのです・・・配列というのは もう ポインタ変数になっていると思っていたのですが それともポインタ としての引渡しは 一次元でしかできないのでしょうか?? それと 教えてくださったプログラムの中の 8行目の tolower というのは もともとC言語で使える関数か何かでしょうか??
補足
本当に毎回ご親切な回答 ありがとうございます! サイズというものを利用するのですね!ということは 三次元でも、 同じようにやればよいのでしょうか? また、今回は例として ”全要素に1足す” というプログラムを書き ましたが、もっと複雑な計算も 上記の書き方を 応用すればよいのでしょうか? たびたびの質問すみません。