- ベストアンサー
2次元配列を戻り値とする関数?
いつもお世話になっています。 角度を入力すると、 2×2の2次元配列を戻す 関数を作りたいのですが、 コンパイルすると、 戻り値の型のところで、 不正な変換だというエラーが出て うまく行きません。 参考書を何度も読み直して 戻り値の型をポインタのポインタにするなど、 いろいろトライしてみたのですが、うまく行きません。 typedef を使う方法も考えましたが、 他にもっとすっきりする方法はないでしょうか? どなたか参考URLをお教えくださるか、 解決策を教えてください。 よろしくお願いします。 ちなみに、この関数は大凡下記の通りです。 double** Matrix(double sita) { double mat[2][2]; mat[0][0]= cos(sita); mat[0][1]= sin(sita); mat[1][0]=-sin(sita); mat[1][1]= cos(sita); return mat; }
- みんなの回答 (15)
- 専門家の回答
質問者が選んだベストアンサー
補足です。 もし、配列にこだわらないのであれば構造体を宣言してその値を返すようにした方が良いかもしれませんね。 struct mat_str { :(配列でも、それぞれ意味のある変数でもOK) }; struct mat_str Matrix (double sita) { struct mat_str mat; : return mat; } 呼び出し側は、 struct mat_str result; result = Matrix(1.0); で、Matrix内のauto変数の値が 呼び出し側の resultに代入されます。 typedef struct mat_str stMat; とすれば見やすくなりますね。
その他の回答 (14)
- leaz024
- ベストアンサー率75% (398/526)
> int b[2][3]; > b=p > を行ったところ、 > 型変換できないというコンパイルエラーになりました。 > つまり、この代入は、一方通行のようなのですが、 > これは、何故なのでしょうか? b は配列変数のアドレスを表しているので、b に何か代入しようというのは、例えば int a と宣言された変数 a に対し、&a = p という代入をするのと同じことなのです。 変数自身のアドレスというのは、例外なく全てが「定数ポインタ」なので、アドレスへの代入はコンパイル時にエラーとなります。 例えば int a と宣言した a のアドレス &a の型は int * const です。 ここでの const は、ポイント先のデータではなく、&a 自身にかかります。 ポイント先である *&a(つまり a )は変更できますが、&a は const なので変更できません。 同様に上記の b の型は、int (* const)[3] です。 b 自身が const であるため、b への代入はできないのです。
- happy_people
- ベストアンサー率30% (25/81)
配列とポインタで困っているところに追い討ちをかけてしまいますが 「配列を返す関数」を定義することはできません。 下のMatrix1はコンパイルに失敗します。 #include <stdio.h> #include <math.h> typedef double matrix_t1[2][2]; /* matrix_t1 Matrix1(double th) { double s = sin(th); double c = cos(th); matrix_t1 m; m[0][0] = c; m[0][1] = s; m[1][0] = -s; m[1][1] = c; return m; } */ typedef struct { double v[2][2]; } matrix_t2; matrix_t2 Matrix2(double th) { double s = sin(th); double c = cos(th); matrix_t2 m; /* printf("%p\n", &m); */ m.v[0][0] = c; m.v[0][1] = s; m.v[1][0] = -s; m.v[1][1] = c; return m; } void main() { matrix_t2 m = Matrix2(1); /* printf("%p\n", &m); */ printf("%g %g\n%g %g\n", m.v[0][0], m.v[0][1], m.v[1][0], m.v[1][1]); }
お礼
happy_people様 アドバイスありがとうございます。 サンプルプログラムまで 教えていただいてとても助かりました。 早速やってみます。 今後ともよろしくお願いします。
- leaz024
- ベストアンサー率75% (398/526)
二次元配列へのポインタについてアドバイスを。 二次元配列 int a[2][3] があった時、この変数 a へのポインタ p の宣言は int (*p)[3] であり、int *p ではありません。 これは a が要素数情報を持つポインタであるのに対し、int * で宣言された p にはそれがないからです。 この要素数情報は型に含まれており、このため a と p は型があわず、代入しようとするとエラーとなるのです。 要素数情報を理解するには、次の「配列とポインタの関係」を見てください。 配列とその配列へのポインタとの関係は非常に簡単です。 int a[10]; int *p; p = a; // p と a は同じ int * 型なので、代入しても問題ない。 p[3] = 5; // a[3] = 5 と同じ。 この関係は、二次元配列になっても同じです。 int a[2][3]; // a へのポインタ p の宣言 p = a; // 問題なく代入できるためには、p と a は同じ型でなければならない。 p[1][0] = 7; // a[1][0] = 7 と同じ。二次元配列へのポインタなのだから、できて当然。 a の型が int * なら、p の宣言は int *p で良いはずですね。 この時 p[1][0] という指定で、正しく a[1][0] にアクセスできるでしょうか? 答えは「否」です。 二次元配列 a の場合、一次元目(右側)の要素数が3個と分かっているので、a[1][0] とするだけで4番目のデータにアクセスできますが、p にはそれがないので、p[1][0] というアクセスができないのです。 確かに p[4] とすれば a[1][0] にアクセスすることもできますが、この方法には何のメリットもありません。一部の入門者が面白いと思うだけです。 a は3個の要素をもつ配列の配列なので、型は int (*)[3] となります。 この型で変数を宣言すればよいので、p の宣言は int (*p)[3] となります。(*p を囲むカッコは必須) こうすることで *p のサイズは12バイト(正しくはintデータ3つ分)となり、p[n][m] というアクセスが可能となるのです。 (つまり、(*(p+n))[m] あるいは、*(*(p+n)+m) として解釈されます。)
お礼
leaz024様 アドバイスありがとうございます。 曖昧に理解していた部分が 厳密に理解できて、とても助かりました。 特に、配列の場合、列数を明示しないと 受け取れないという「しくみ」が良くわかりました。 前に参考書で見たような気がしたのですが、 上記のように丁寧には書かれていなかったので、 とても勉強になりました。 これを応用して、 3次元配列(a[2][3][4])の場合を 試行したところ、 int (*p)[3][4] p = a でばっちりうまく行きました。 n次元でも大丈夫そうです。 本当にありがとうございました。
補足
アドバイスありがとうございます。 1点だけ、分からない点があるので、 教えていただければありがたいです。 上の例で、 p=a で受け取った後、 pは、あたかもaと全く同じように 配列操作が行えることが確認できました。 にもかかわらず、 int b[2][3]; b=p を行ったところ、 型変換できないというコンパイルエラーになりました。 つまり、この代入は、一方通行のようなのですが、 これは、何故なのでしょうか? よろしくお願いします。
- nagare
- ベストアンサー率33% (280/831)
>*(mat+0) と mat[0][0] >は同じではありません。 >mat[0] ならば同じなのですが。 はい そのとおりです。 メモリ上の話と同じということです。 読み替えてください ませ main() { double mata[2][2]; double *matb; matb = malloc(sizeof(double) * 2 * 2); Matrix(&mata[0][0] ,90); Matrix(matb ,90); };
- ranx
- ベストアンサー率24% (357/1463)
質問に対する回答ではありませんが、一言だけ。 *(mat+0) と mat[0][0] は同じではありません。 mat[0] ならば同じなのですが。
- nagare
- ベストアンサー率33% (280/831)
おかしいなぁー return &mat[0][0]; でどうでしょう(悔しい) 但し、私の方法だとエラーはでない思います(voidにしたので) void Matrix(double *mat ,double sita) { *(mat+0)= cos(sita); *(mat+1)= sin(sita); *(mat+2)=-sin(sita); *(mat+3)= cos(sita); return; }; これは、yatokesa様の void Matrix (double mat[2][], double sita) mat[0][0]= cos(sita); mat[0][1]= sin(sita); mat[1][0]=-sin(sita); mat[1][1]= cos(sita); } とまったく同じです(好みの差です) *(mat+0)はmat[0][0] *(mat+1)はmat[0][1] *(mat+2)はmat[1][0] *(mat+3)はmat[1][1]
- yatokesa
- ベストアンサー率40% (201/496)
> double tt[2][2]; >tt=Matrix(PI/2); 呼び出し側のauto変数に配列として代入したいのなら、素直に呼び出し側の領域(tt)を渡した方が良いのではないでしょうか? void Matrix (double mat[2][], double sita) mat[0][0]= cos(sita); mat[0][1]= sin(sita); mat[1][0]=-sin(sita); mat[1][1]= cos(sita); } 関数は1つの値しか返せませんので、呼び出し側で用意した配列(複数の領域)に代入するようなコーディングはできないんです。 戻り値としてコーディングしたいのなら、先の私の補足のように struct として一つの領域にしてやりとりをすることをお薦めします。
お礼
yatokesa様 アドバイスありがとうございます。 上記の方法が呼び出し側での取り扱い コーディング量から考えて 一番良さそうな気がしてきました。 おかげさまで、いろいろな方法で 解決できる目処が立ちました。 お忙しい中、 ご教示ありがとうございました。
- ranx
- ベストアンサー率24% (357/1463)
> double tt[2][2]; > tt=Matrix(PI/2); > のように配列変数のままにすると > 型変換不能でコンパイルエラーになります。 配列は一種の「定数」であって「変数」ではありませんから、 そこへ何かを代入するという操作はできません。 > 何か良い方法がありましたら、 > 教えていただければありがたいです。 そのような方法は無いと思います。・・・というか、配列 変数にこだわらないのが良い方法だと思います。
お礼
ranx様 分かりました。 データは得られていますので、 あまり形式にこだわらず作業を続けることにします。 本当に助かりました。 どうもありがとうございました。
- nagare
- ベストアンサー率33% (280/831)
これは変ですねぇー ファイルの拡張子は'.C'ですか? '.CC'ですか? '.CC'なら'.C'でコンパイルしてください
お礼
nagare様 アドバイスありがとうございます。 ファイルは、CCですが、 クラス等を使っているので、 Cでのコンパイルは エラーがたくさん出て、 うまく行きませんでした。 そこで、Unix(Compaq)でやってみたのですが、 やはり、コンパイルで型変換エラーが出ました。 ranx様の方法にした場合は、 うまく行きました。 nagare様の方法は非常にシンプルで 個人的には、この形で実現できたら とても良いと思っていますので、 今後の検討課題にしたいと思います。 当面、yotaka様やranx様の方法で 対応していこうと思います。 いろいろとご教示ありがとうございました。
- ranx
- ベストアンサー率24% (357/1463)
double (*Matrix(double sita))[2] { static double mat[2][2]; mat[0][0] = cos(sita); mat[0][1] = sin(sita); mat[1][0] = -sin(sita); mat[1][1] = cos(sita); return mat; } 呼び出す度に値は上書きされます。 それが嫌な場合は、その都度malloc()等で記憶領域を確保する 必要があります。
補足
ranx様 アドバイスありがとうございます。 上記方法でうまく行きました。 大変助かりました。 この場合、呼び出し側で戻り値を受ける変数も double (*tt)[2]; と宣言すれば良いことは分かりましたが、 double tt[2][2]; tt=Matrix(PI/2); のように配列変数のままにすると 型変換不能でコンパイルエラーになります。 何か良い方法がありましたら、 教えていただければありがたいです。 よろしくお願いします。
- 1
- 2
お礼
leaz024様 アドバイスありがとうございます。 おかげさまで、 ポインタと配列に関して深い理解が得られました。 本当に助かりました。 今後ともよろしくお願いします。