2次元配列のアドレスの表現法

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
shimitakax

2次元配列のアドレスの表現法

#1

投稿記事 by shimitakax » 4年前

2次元配列のアドレスの表現法について質問です。
2次元配列dの先頭アドレスを求める式には
実は5つあり【d】【&d[0]】【d[0]】【*d】【&d[0][0]】が該当すると聞いたのですが,なぜこれで表せるかわからないです。
教えて頂けると幸いです。

コード:

int d[4][4] = {	{9,8,7},
			{6,5,4},
			{3,2,1} };

アバター
Tatu
記事: 445
登録日時: 9年前
住所: 北海道

Re: 2次元配列のアドレスの表現法

#2

投稿記事 by Tatu » 4年前

私は以下のような理由で表現できるのではないかと思います。

配列名は配列の先頭の要素のアドレスになる。
dは配列の配列であるため、先頭の配列のアドレスを指す。
先頭の配列のアドレスは先頭の配列の先頭の要素のアドレスなので
dは先頭アドレスになる。

&d[0]は先頭の配列のアドレスなので先頭アドレスになる。

d[0]は先頭の配列の名前と考えられ、その先頭の要素のアドレスを指すので先頭アドレスになる。

*d=*(&d[0])=d[0]なので先頭アドレスになる。

&d[0][0]は先頭の配列の先頭の要素のアドレスなので先頭アドレスになる。

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: 2次元配列のアドレスの表現法

#3

投稿記事 by YuO » 4年前

まず,基本事項として,配列型のオブジェクトが式中にでてきた場合,いくつかの演算子の対象になる場合(sizeofやtypeid,単項&など)を除いて,配列型のオブジェクトはその先頭要素へのポインタに変換されます。
つまり,aが配列型のオブジェクトについた識別子の場合,ソースコード中のaと&a[0]は上記の例外を除くと同じ意味です。

その上で,二次元配列,つまり配列の配列型のオブジェクトに関して,次のような関係になります。

コード:

int d[4][4];
に対して,
  • 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]と同じ意味になります。

ちなみに,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++で,と書いています。

shimitakax

Re: 2次元配列のアドレスの表現法

#4

投稿記事 by shimitakax » 4年前

すみません。
*dはdが&d[0]というのがいまいちよく理解できません。
それとなぜdが&d[0]に変換されるのかも良く解りません…

アバター
Tatu
記事: 445
登録日時: 9年前
住所: 北海道

Re: 2次元配列のアドレスの表現法

#5

投稿記事 by Tatu » 4年前

添字演算子[]の定義から考えると
dと*(d+i)は同じです。

i=0の場合を考えてd[0]と*dは同じです。

d[0]と*dの両方に&演算子をつけて
&d[0]と&(*d)は同じです。

&(*d)はdと同じですから
&d[0]とdは同じです。

閉鎖

“C言語何でも質問掲示板” へ戻る