• ベストアンサー

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; }

  • zico
  • お礼率60% (48/80)

質問者が選んだベストアンサー

  • ベストアンサー
  • yatokesa
  • ベストアンサー率40% (201/496)
回答No.4

補足です。 もし、配列にこだわらないのであれば構造体を宣言してその値を返すようにした方が良いかもしれませんね。 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)
回答No.15

> 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 への代入はできないのです。

zico
質問者

お礼

leaz024様 アドバイスありがとうございます。 おかげさまで、 ポインタと配列に関して深い理解が得られました。 本当に助かりました。 今後ともよろしくお願いします。

回答No.14

配列とポインタで困っているところに追い討ちをかけてしまいますが 「配列を返す関数」を定義することはできません。 下の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]); }

zico
質問者

お礼

happy_people様 アドバイスありがとうございます。 サンプルプログラムまで 教えていただいてとても助かりました。 早速やってみます。 今後ともよろしくお願いします。

  • leaz024
  • ベストアンサー率75% (398/526)
回答No.13

二次元配列へのポインタについてアドバイスを。 二次元配列 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) として解釈されます。)

zico
質問者

お礼

leaz024様 アドバイスありがとうございます。 曖昧に理解していた部分が 厳密に理解できて、とても助かりました。 特に、配列の場合、列数を明示しないと 受け取れないという「しくみ」が良くわかりました。 前に参考書で見たような気がしたのですが、 上記のように丁寧には書かれていなかったので、 とても勉強になりました。 これを応用して、 3次元配列(a[2][3][4])の場合を 試行したところ、 int (*p)[3][4] p = a でばっちりうまく行きました。 n次元でも大丈夫そうです。 本当にありがとうございました。

zico
質問者

補足

アドバイスありがとうございます。 1点だけ、分からない点があるので、 教えていただければありがたいです。 上の例で、 p=a で受け取った後、 pは、あたかもaと全く同じように 配列操作が行えることが確認できました。 にもかかわらず、 int b[2][3]; b=p を行ったところ、 型変換できないというコンパイルエラーになりました。 つまり、この代入は、一方通行のようなのですが、 これは、何故なのでしょうか? よろしくお願いします。

  • nagare
  • ベストアンサー率33% (280/831)
回答No.12

>*(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)
回答No.11

質問に対する回答ではありませんが、一言だけ。 *(mat+0) と mat[0][0] は同じではありません。 mat[0] ならば同じなのですが。

  • nagare
  • ベストアンサー率33% (280/831)
回答No.10

おかしいなぁー 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)
回答No.9

> 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 として一つの領域にしてやりとりをすることをお薦めします。

zico
質問者

お礼

yatokesa様 アドバイスありがとうございます。 上記の方法が呼び出し側での取り扱い コーディング量から考えて 一番良さそうな気がしてきました。 おかげさまで、いろいろな方法で 解決できる目処が立ちました。 お忙しい中、 ご教示ありがとうございました。

  • ranx
  • ベストアンサー率24% (357/1463)
回答No.8

> double tt[2][2]; > tt=Matrix(PI/2); > のように配列変数のままにすると > 型変換不能でコンパイルエラーになります。 配列は一種の「定数」であって「変数」ではありませんから、 そこへ何かを代入するという操作はできません。 > 何か良い方法がありましたら、 > 教えていただければありがたいです。 そのような方法は無いと思います。・・・というか、配列 変数にこだわらないのが良い方法だと思います。

zico
質問者

お礼

ranx様 分かりました。 データは得られていますので、 あまり形式にこだわらず作業を続けることにします。 本当に助かりました。 どうもありがとうございました。

  • nagare
  • ベストアンサー率33% (280/831)
回答No.7

これは変ですねぇー ファイルの拡張子は'.C'ですか? '.CC'ですか? '.CC'なら'.C'でコンパイルしてください

zico
質問者

お礼

nagare様 アドバイスありがとうございます。 ファイルは、CCですが、 クラス等を使っているので、 Cでのコンパイルは エラーがたくさん出て、 うまく行きませんでした。 そこで、Unix(Compaq)でやってみたのですが、 やはり、コンパイルで型変換エラーが出ました。 ranx様の方法にした場合は、 うまく行きました。 nagare様の方法は非常にシンプルで 個人的には、この形で実現できたら とても良いと思っていますので、 今後の検討課題にしたいと思います。 当面、yotaka様やranx様の方法で 対応していこうと思います。 いろいろとご教示ありがとうございました。

  • ranx
  • ベストアンサー率24% (357/1463)
回答No.6

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()等で記憶領域を確保する 必要があります。

zico
質問者

補足

ranx様 アドバイスありがとうございます。 上記方法でうまく行きました。 大変助かりました。 この場合、呼び出し側で戻り値を受ける変数も  double (*tt)[2]; と宣言すれば良いことは分かりましたが、  double tt[2][2]; tt=Matrix(PI/2); のように配列変数のままにすると 型変換不能でコンパイルエラーになります。 何か良い方法がありましたら、 教えていただければありがたいです。 よろしくお願いします。

関連するQ&A

  • 配列を関数の戻り値に使う

    こんばんわ。 PHP5で配列を関数の戻り値として返す方法がわからず今回投稿させていただきました。 配列は1次元で 1,2,3 の値が a という配列に格納されています。 呼び出された関数(testメソッド)で a配列を返し、bで受けようとするのですが、bの値には NULL となっています。 返す際に return $a; としており、 $b=$obj-〉test(); で、aをbで受けようと考えております。 配列を返す場合は普通にretuneで返すだけではダメなのでしょうか。 解決方法がお分りの方がいらっしゃいましたらご教授のほうおねがいします。

    • ベストアンサー
    • PHP
  • 関数ポインタを返す関数の型をtypedefする方法

    C言語について質問します。 ある関数を定義するとします。 その関数は引数としてintを一つ取り、返値としてその関数と同じ型の関数へのポインタを返すようにしたいのですが、どのように書けばよいのでしょうか? そして、その関数の型をtypedefで定義したいです。 例えば、FNをtypedefしたいその関数の型だとすると、 typedef FN (*FN)(int); のようなFNを定義したいのですが、上のように書いても当然コンパイラ(VC9)に怒られます。 最悪、 typedef void* (*FN)(int); とvoidポインタを返すように定義しておいて、そのポインタを返値として受け取った側でFNにキャストし直す方法で対処できなくもないですが、ちょっと強引過ぎる気がします。 何かいい方法はあるのでしょうか? boost::functionあたりを使えばできそうな、そうでもないような気がしますが、できれば純粋なCでの解決法を望みます。 よろしくお願いします。

  • 二次元配列が、勝手に一次元配列になってしまう

    Excelのマクロで、二次元配列を格納したバリアント型を戻り値とする関数を作ったのですが、… 二次元型配列のひとつの要素数が1の時、受け取ったバリアント型変数は、一次元配列になっています。 列ベクトルなら、それもありかな~と思うのですが、なんで、行ベクトルまで、一次元配列にするんだよ~って、困っています。 シートから関数を呼んだ場合は、列ベクトルは列ベクトル、行ベクトルは行ベクトルとして、表示されるので、マクロの中で関数を呼び出した場合も、行列の情報を保持できる方法があるんじゃないかと思ったのですが。 どなたか、ご教示頂けるとありがたいです。 よろしくお願いします。

  • 2次元配列からダブルポインタへの合わせ方

    固定要素数の行列の処理ですが、subでセグメントエラーを起こしています。 アドレスを出力してみると、subでは元々確保したアドレスでは無いところを指しています。 呼び出し側では「行列の先頭のアドレス」を明示したつもりだったのですが、どこが間違っているのでしょうか。少し長いですが、ソースコードと出力を示します。 typedefのところとsubのIFは変更できないので、double[3][3]とdouble** のIFを合わせないといけないのですが、合わせ方がよくわかりません。 よろしくお願いします。 ========================================= #include <stdio.h> #include <string.h> typedef double Matrix[3][3]; void sub( const double** mat ); int main(void) {  int ret = 0;  int i, j;  Matrix mat = { { 0., 1., 2. },      { 3., 4., 5. },      { 6., 7., 8. } };    for( i = 0; i < 3; i++ ){   for( j = 0; j < 3; j++ ){    printf( "main : mat[%d][%d] = %f\n", i, j, mat[i][j] );    printf( "main : &mat[%d][%d] = %p\n", i, j, &mat[i][j] );   }  }  printf( "\n" );  sub( &mat[0][0] );  return ret; } void sub( const double** mat ) {  int i, j;  printf("---- &mat = %p\n", &mat );  for( i = 0; i < 3; i++ ){   for( j = 0; j < 3; j++ ){    printf( "sub : mat[%d][%d] = %f\n", i, j, mat[i][j] );    printf( "sub : &mat[%d][%d] = %p\n", i, j, &mat[i][j] );   }  }  printf( "\n" ); } ========================================= main : mat[0][0] = 0.000000 main : &mat[0][0] = 0x7ffffca7bca0 main : mat[0][1] = 1.000000 main : &mat[0][1] = 0x7ffffca7bca8 main : mat[0][2] = 2.000000 main : &mat[0][2] = 0x7ffffca7bcb0 main : mat[1][0] = 3.000000 main : &mat[1][0] = 0x7ffffca7bcb8 main : mat[1][1] = 4.000000 main : &mat[1][1] = 0x7ffffca7bcc0 main : mat[1][2] = 5.000000 main : &mat[1][2] = 0x7ffffca7bcc8 main : mat[2][0] = 6.000000 main : &mat[2][0] = 0x7ffffca7bcd0 main : mat[2][1] = 7.000000 main : &mat[2][1] = 0x7ffffca7bcd8 main : mat[2][2] = 8.000000 main : &mat[2][2] = 0x7ffffca7bce0 ---- &mat = 0x7ffffca7bc68 セグメンテーション違反です

  • 関数の戻り値を構造体配列で。 VC++2005

    構造体配列で値を戻すようコードを試行錯誤しています。ご教授下さい。 開発環境は、XPpro VisualStudio2005アカデミックです。 コード(抜粋) struct double3d{ double x, y, z; }; //倍精度浮動小数三次元デカルト要素構造体型 //COAクラスのpublicで宣言。COAクラスを「psmf」でnewしています。 double3d COA::getu(double3d tempGP) { double3d tempCOA[2][2][2];  //構造体配列 中略 return tempCOA;  //*1 //tempCOAの先頭アドレスを返す } 別のインスタンスから、 (double3d tCOA[2][2][2];) tCOA = psmf->getu(GPosit); と呼び出しています。(関数の呼び出し部分の動作は確認済) この状況でコンパイルすると、*1の行で 'double3d::double3d(const double3d &)' : 1 番目の引数を 'double3d [2][2][2]' から 'const double3d &' に変換できません。(理由: 'double3d [2][2][2]' から 'const double3d' へは変換できません。コンストラクタはソース型を持てません、またはコンストラクタのオーバーロードの解決があいまいです。) とエラーになります。 自分としては、戻り値に構造体配列の先頭アドレスを返しているつもりなのですが…。 尚、*演算子、&演算子で戻しても同様のエラーです。 様々なウェブサイトを拝見したのですが、構造体配列を戻り値として解説しているサイト様も少なく、手詰まりの状態です。 お手数をおかけしますが、宜しく御願いいたします。

  • 2次元配列のnew

    4x4行列のデータがいくらか書いてあるファイルから、読み込んでvector配列へ保存するプログラムを考えています。 vector配列は、float[4][4]の先頭ポインタの配列です。 宣言はこんな感じで、コンパイルエラーは出ませんでした。 vector<float[4][4]> matrix; つぎに読み込み部分で、下のような感じです。 4x4行列が見つかるたびにこれが実行されます。 GetFloatToken()は、ファイルから要素をひとつ取り出す関数です。 float m[4][4] = new float[4][4]; for(int g=0; g<4; g++) for(int r=0; r<4; r++) m[g][r]=GetFloatToken(); matrix.push_back(m); newの行とpush_backしてる行でエラーが出ました。 自分が思うに、m[4][4]のとこの、newからの受け取りの仕方が悪い気がしました。 そこで、*m[4] や **m とかにして試してみましたがうまくいきませんでした。 どうすればいいんでしょうか。 もしかして、c++では多次元配列のnewは無理なんでしょうか。 わかる方がいましたら、どうか教えてください。 補足: あとで行列の計算をするのが楽なので、float[4][4]の形は変えたくないです。 もし、多次元配列のnewが無理ということなら別の策を考えます。

  • 戻り値について

    プログラミング初心者です。 よろしくお願いします。 C++を使っています。 早速なのですが、以下にプログラムを記載します。 ◎1---------------------------------------- #include<stdio.h> main() { char ss[256]; gets(ss); puts(ss); } ---------------------------------------- ◎1を実行すると、「型指定子がありません - int と仮定しました。メモ: C++ は int を既定値としてサポートしていません」と表示されます。 参考書には、「戻り値のvoidがないと勝手に戻り値の関数をint型と解釈する。関数の宣言と定義が合わないとコンパイル時にエラーとなる」とあったのですが、 ◎2------------------------------------- #include<stdio.h> int main() { char ss[256]; gets(ss); puts(ss); } ------------------------------------- 以上の◎2だと、なぜ実行出来るかよくわかりません。 本当に初心者的な質問ですいませんが、教えていただけると嬉しいです。

  • long double型の戻り値を持つ関数について

    文字列を浮動小数点に変換したいと思っています。 StrToFloat()を用いたのですが、有効数値がケタ落ちしてしまいました。 そこで、次のように関数を定義して実行したところ、やはり戻り値の値がケタ落ちしました。 long double StrToValue(AnsiString str) { ・・・・・ return value; } 具体的にはlong doubleの有効数値がdouble型の有効数値にまで落ちてしまっています。 次にポインタを使い、次のように変更したのですが、結果は同じでした。 void StrToValue(AnsiString str, long double *value) { ・・・・・ *value=・・・; ・・・・・ } どなたか、この解決方法と、できれば理由を教えてください。 なお使用した言語は、C++Builder 5 環境はWindows 98 です。

  • 二次元配列の引数渡し

    二次元配列を関数の引数として渡し、 その配列を戻り値として呼び出し元の変数に返したいです。 具体的にはルンゲクッタ法で微分する関数の引数を行列で扱いたいです。 for (i=0;i<N;i++){ for (j=0;j<N;j++){ y[i][j]=rand(); } }   K1=h*ff(t,y); K2=h*ff(t+h/2,y+K1/2); K3=h*ff(t+h/2,y+K2/2); K4=h*ff(t+h,y+K3); Ka=(K1+2*K2+2*K3+K4)/6; double ff(int t,double y[][]){ ここで return y[][]; のような形で二次元配列を戻り値として変数に返したいのです。 } C言語初心者なのでいまいちよくわかりません、 宜しくお願いいたします。

  • スプレッドシートのGASの関数で複数戻り値取得

    googleのスプレッドシートを使っています。 Google Apps Scriptでデータの取得を行っているのですが、 1つの関数から複数の戻り値を得るような方法はありますでしょうか? マイコンのC言語などをやった際に、ポインタ変数を関数の引数に設定して、その関数の引数に配列変数などをアドレス指定で設定することで複数の戻り値を得るような方法があったと思うのですが、GASでも同様の方法などはありますでしょうか? どうぞ、ご教示の程よろしくお願い致します。

専門家に質問してみよう