あなたの「必」の書き順を教えてください

いつもお世話になっています。

角度を入力すると、
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;
}

A 回答 (15件中1~10件)

補足です。


もし、配列にこだわらないのであれば構造体を宣言してその値を返すようにした方が良いかもしれませんね。
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;
とすれば見やすくなりますね。
    • good
    • 1

> 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 への代入はできないのです。
    • good
    • 0
この回答へのお礼

leaz024様

アドバイスありがとうございます。

おかげさまで、
ポインタと配列に関して深い理解が得られました。

本当に助かりました。
今後ともよろしくお願いします。

お礼日時:2002/07/22 13:52

配列とポインタで困っているところに追い討ちをかけてしまいますが


「配列を返す関数」を定義することはできません。
下の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]);
}
    • good
    • 0
この回答へのお礼

happy_people様
アドバイスありがとうございます。

サンプルプログラムまで
教えていただいてとても助かりました。
早速やってみます。

今後ともよろしくお願いします。

お礼日時:2002/07/19 10:36

二次元配列へのポインタについてアドバイスを。



二次元配列 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) として解釈されます。)

この回答への補足

アドバイスありがとうございます。

1点だけ、分からない点があるので、
教えていただければありがたいです。
上の例で、

p=a

で受け取った後、
pは、あたかもaと全く同じように
配列操作が行えることが確認できました。

にもかかわらず、

int b[2][3];
b=p

を行ったところ、
型変換できないというコンパイルエラーになりました。
つまり、この代入は、一方通行のようなのですが、
これは、何故なのでしょうか?

よろしくお願いします。

補足日時:2002/07/19 10:50
    • good
    • 1
この回答へのお礼

leaz024様
アドバイスありがとうございます。

曖昧に理解していた部分が
厳密に理解できて、とても助かりました。
特に、配列の場合、列数を明示しないと
受け取れないという「しくみ」が良くわかりました。

前に参考書で見たような気がしたのですが、
上記のように丁寧には書かれていなかったので、
とても勉強になりました。

これを応用して、
3次元配列(a[2][3][4])の場合を
試行したところ、
int (*p)[3][4]
p = a
でばっちりうまく行きました。
n次元でも大丈夫そうです。

本当にありがとうございました。

お礼日時:2002/07/19 10:45

>*(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);
};
    • good
    • 0

質問に対する回答ではありませんが、一言だけ。


*(mat+0) と mat[0][0]
は同じではありません。
mat[0] ならば同じなのですが。
    • good
    • 0

おかしいなぁー



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]
    • good
    • 0

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

yatokesa様

アドバイスありがとうございます。
上記の方法が呼び出し側での取り扱い
コーディング量から考えて
一番良さそうな気がしてきました。

おかげさまで、いろいろな方法で
解決できる目処が立ちました。

お忙しい中、
ご教示ありがとうございました。

お礼日時:2002/07/17 14:58

> double tt[2][2];


> tt=Matrix(PI/2);
> のように配列変数のままにすると
> 型変換不能でコンパイルエラーになります。

配列は一種の「定数」であって「変数」ではありませんから、
そこへ何かを代入するという操作はできません。

> 何か良い方法がありましたら、
> 教えていただければありがたいです。

そのような方法は無いと思います。・・・というか、配列
変数にこだわらないのが良い方法だと思います。
    • good
    • 0
この回答へのお礼

ranx様

分かりました。
データは得られていますので、
あまり形式にこだわらず作業を続けることにします。

本当に助かりました。
どうもありがとうございました。

お礼日時:2002/07/17 15:01

これは変ですねぇー



ファイルの拡張子は'.C'ですか? '.CC'ですか?
'.CC'なら'.C'でコンパイルしてください
    • good
    • 0
この回答へのお礼

nagare様

アドバイスありがとうございます。
ファイルは、CCですが、
クラス等を使っているので、
Cでのコンパイルは
エラーがたくさん出て、
うまく行きませんでした。

そこで、Unix(Compaq)でやってみたのですが、
やはり、コンパイルで型変換エラーが出ました。

ranx様の方法にした場合は、
うまく行きました。

nagare様の方法は非常にシンプルで
個人的には、この形で実現できたら
とても良いと思っていますので、
今後の検討課題にしたいと思います。

当面、yotaka様やranx様の方法で
対応していこうと思います。

いろいろとご教示ありがとうございました。

お礼日時:2002/07/17 15:08

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています


おすすめ情報