2次元配列のアドレスの表現法について質問です。
2次元配列dの先頭アドレスを求める式には
実は5つあり【d】【&d[0]】【d[0]】【*d】【&d[0][0]】が該当すると聞いたのですが,なぜこれで表せるかわからないです。
教えて頂けると幸いです。
2次元配列のアドレスの表現法
Re: 2次元配列のアドレスの表現法
私は以下のような理由で表現できるのではないかと思います。
配列名は配列の先頭の要素のアドレスになる。
dは配列の配列であるため、先頭の配列のアドレスを指す。
先頭の配列のアドレスは先頭の配列の先頭の要素のアドレスなので
dは先頭アドレスになる。
&d[0]は先頭の配列のアドレスなので先頭アドレスになる。
d[0]は先頭の配列の名前と考えられ、その先頭の要素のアドレスを指すので先頭アドレスになる。
*d=*(&d[0])=d[0]なので先頭アドレスになる。
&d[0][0]は先頭の配列の先頭の要素のアドレスなので先頭アドレスになる。
配列名は配列の先頭の要素のアドレスになる。
dは配列の配列であるため、先頭の配列のアドレスを指す。
先頭の配列のアドレスは先頭の配列の先頭の要素のアドレスなので
dは先頭アドレスになる。
&d[0]は先頭の配列のアドレスなので先頭アドレスになる。
d[0]は先頭の配列の名前と考えられ、その先頭の要素のアドレスを指すので先頭アドレスになる。
*d=*(&d[0])=d[0]なので先頭アドレスになる。
&d[0][0]は先頭の配列の先頭の要素のアドレスなので先頭アドレスになる。
Re: 2次元配列のアドレスの表現法
まず,基本事項として,配列型のオブジェクトが式中にでてきた場合,いくつかの演算子の対象になる場合(sizeofやtypeid,単項&など)を除いて,配列型のオブジェクトはその先頭要素へのポインタに変換されます。
つまり,aが配列型のオブジェクトについた識別子の場合,ソースコード中のaと&a[0]は上記の例外を除くと同じ意味です。
その上で,二次元配列,つまり配列の配列型のオブジェクトに関して,次のような関係になります。 に対して,
型を無視して式中での値が同じであっても,意味が異なるので,配列型オブジェクトとポインタや要素の関係は一度整理した方がよいと思います。
個人的には「アドレス」という言葉は,意味の中に型を含まないことが多いため,使わないことを推奨します。
ちなみに,Visual C++ならば,なんてコードを実行してみると,その式自体の型と,その式が式中ででてきた場合の型がわかるかと思います。
つまり,aが配列型のオブジェクトについた識別子の場合,ソースコード中のaと&a[0]は上記の例外を除くと同じ意味です。
その上で,二次元配列,つまり配列の配列型のオブジェクトに関して,次のような関係になります。 に対して,
- d : int[4][4]型 : オブジェクトそのもの→&d[0]に変換される
- &d : int (*) [4][4]型 : オブジェクトそのものへのポインタ
- d[0] : int[4]型 : オブジェクトの先頭要素である配列→&d[0][0]に変換される
- &d[0] : int (*)[4]型 : オブジェクトの先頭要素へのポインタ
- d[0][0] : int型 : オブジェクトの先頭要素である配列の先頭要素
- &d[0][0] : int *型: オブジェクトの先頭要素である配列の先頭要素へのポインタ
型を無視して式中での値が同じであっても,意味が異なるので,配列型オブジェクトとポインタや要素の関係は一度整理した方がよいと思います。
個人的には「アドレス」という言葉は,意味の中に型を含まないことが多いため,使わないことを推奨します。
オフトピック
ちなみに,*dはdが&d[0]になるので,*&d[0],つまりd[0]と同じ意味になります。
そして,d[0]は上記にあるとおり,&d[0][0]に変換されます。
よって,*dは&d[0][0]と同じ意味になります。
そして,d[0]は上記にあるとおり,&d[0][0]に変換されます。
よって,*dは&d[0][0]と同じ意味になります。
ちなみに,Visual C++ならば,
#include <iostream>
#include <typeinfo>
#include <string>
int main(void)
{
int d[4][6] = { 0 }; // 要素数が違う方がどちらが残ったかがわかりやすいため,要素数を変えている
// 直接typeid演算子の対象にした場合。配列型は配列型のまま扱われる。
std::cout << "typeid / d : " << typeid(d).name() << std::endl;
std::cout << "typeid / &d : " << typeid(&d).name() << std::endl;
std::cout << "typeid / *d : " << typeid(*d).name() << std::endl;
std::cout << "typeid / d[0] : " << typeid(d[0]).name() << std::endl;
std::cout << "typeid / &d[0] : " << typeid(&d[0]).name() << std::endl;
std::cout << "typeid / *d[0] : " << typeid(*d[0]).name() << std::endl;
std::cout << "typeid / d[0][0] : " << typeid(d[0][0]).name() << std::endl;
std::cout << "typeid / &d[0][0] : " << typeid(&d[0][0]).name() << std::endl;
// 式中で使う例
auto exp_d = d;
auto exp_addr_d = &d;
auto exp_ind_d = *d;
auto exp_d_0 = d[0];
auto exp_addr_d_0 = &d[0];
auto exp_ind_d_0 = *d[0];
auto exp_d_0_0 = d[0][0];
auto exp_addr_d_0_0 = &d[0][0];
std::cout << "expreesion / d : " << typeid(exp_d).name() << std::endl;
std::cout << "expreesion / &d : " << typeid(exp_addr_d).name() << std::endl;
std::cout << "expreesion / *d : " << typeid(exp_ind_d).name() << std::endl;
std::cout << "expreesion / d[0] : " << typeid(exp_d_0).name() << std::endl;
std::cout << "expreesion / &d[0] : " << typeid(exp_addr_d_0).name() << std::endl;
std::cout << "expreesion / *d[0] : " << typeid(exp_ind_d_0).name() << std::endl;
std::cout << "expreesion / d[0][0] : " << typeid(exp_d_0_0).name() << std::endl;
std::cout << "expreesion / &d[0][0] : " << typeid(exp_addr_d_0_0).name() << std::endl;
return 0;
}
オフトピック
gccのstd::typeinfo::nameはVC++ほど直接的な出力をしてくれないので,VC++で,と書いています。
Re: 2次元配列のアドレスの表現法
すみません。
*dはdが&d[0]というのがいまいちよく理解できません。
それとなぜdが&d[0]に変換されるのかも良く解りません…
*dはdが&d[0]というのがいまいちよく理解できません。
それとなぜdが&d[0]に変換されるのかも良く解りません…
Re: 2次元配列のアドレスの表現法
添字演算子[]の定義から考えると
dと*(d+i)は同じです。
i=0の場合を考えてd[0]と*dは同じです。
d[0]と*dの両方に&演算子をつけて
&d[0]と&(*d)は同じです。
&(*d)はdと同じですから
&d[0]とdは同じです。
dと*(d+i)は同じです。
i=0の場合を考えてd[0]と*dは同じです。
d[0]と*dの両方に&演算子をつけて
&d[0]と&(*d)は同じです。
&(*d)はdと同じですから
&d[0]とdは同じです。