参照 (情報工学)

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内検索

情報工学における参照(さんしょう、: referenceリファレンス)は、他の場所にあるデータを指している情報を含む小さなオブジェクトであり、それ自身の中に(指している)データ自体を含まない。参照の指すを取り出すことをデリファレンス (dereference)と呼ぶ(間接参照も参照)。参照は様々なデータ構造を構成する基本要素であり、プログラム内の各部で情報をやり取りするための基本でもある。

なお、C++には、参照型というものがあるが、以下で説明するのはC++のそれではなく、一般概念である。C++の参照については、ポインタ (プログラミング)#参照を参照のこと。

住所を使った例え話[編集]

参照は家の住所に似ている。住所は非常に小さな識別子であり、それが指しているモノにはさらに豊富な情報があるだろう。例えば、その家を見れば色がわかるが、住所だけでは色はわからない。住所は単に家を見つけることを可能にするだけである。しかし、もし家の色が知りたければ、住所さえあれば見つけ出して実際にその家を見ればよい。つまり、住所は家の色を知るための充分な情報源となる。住所から家を探すことは、参照をデリファレンスすることに似ている。

もっと複雑な例として、引越しの度に新しい家の住所を古い家に残しておくとする。誰かが最初の家を訪ねると、置いてある住所から次の家へ次の家へとたどっていき最終的に現在の家にたどり着くことができる。これは参照を使用した単純な線形リストに似ている。

住所の別の利点は、それが実際の家よりもずっと扱いやすいことである。例えば、町内の人々を姓の五十音順に並べたいとしよう。ひとつの方法として、巨大なクレーンを使って町内の家を全部物理的に並べ替える方法がある。もっと簡単な方法としては、町内の人たちの住所のリストを作り、姓の五十音順にそれを並べ替えるという方法がある。参照にも同様の利点がある。データへの参照を操作することによってデータ自体を変更することなく様々なことができるし、場合によってはその方が効率的である。

日常生活は参照の例であふれている。電話番号、電子メールアドレス、URLなどなど。いずれも遠隔にあるリソースを指し、それらへのアクセスを可能とする。

参照の利点[編集]

参照によって、オブジェクトを格納する場所、格納方法、コード内での引き渡しなどの柔軟性が増す。参照によって実際のデータにアクセスできるなら、データ自体を移動させる必要はない。また、複数のコードが参照によってひとつのデータを共有することもできる。

参照の機構の実装は様々だが、最近のほとんど全てのプログラミング言語が備える共通の基本機能である。直接的に参照を使用していない言語も内部的に(あるいは暗黙のうちに)参照を使用している。例えば参照渡しの呼び出し規則は、その言語が参照を明示的に使用しているかどうかに関わらず実装可能である。

ポインタはオブジェクトのメモリ上のアドレスだけを格納したものである。これは最も基本的で間違えやすい参照だが、最も強力で効率的な参照でもある。スマートポインタ不透明データ構造の一種で、ポインタのように働くが、特定のメソッドを通さないとアクセスできない。

ファイルハンドルあるいはハンドルはファイルの内容を抽象化する参照である。それはファイルへのロックを要求する際にはファイル自体を指すと同時に、ファイルを読む際にはファイル内の特定の位置を指す。

形式表現[編集]

より一般化すると、参照は、あるデータの一意の検索を可能とする別のデータとみなすことができる。これにはデータベース主キー連想配列のキーなども含まれる。データの集合 D について、D から D ∪ {null} への一意に定まる関数が参照の定義となる。ここで null は意味のあるものを指していないデータである。

このような関数の別の表現として、「到達可能性グラフ; reachability graph」と呼ばれる有向グラフがある。ここで、各データは頂点として表され、データ u から データv へのエッジがあるとき、uv を参照している(グラフ理論)。最大出次数は 1 である。このように参照をグラフとして捉えることはガベージコレクションで到達不可能なオブジェクトからのアクセスを分離するのに有効である。

外部収納と内部収納[編集]

多くのデータ構造の中で、大きく複雑なオブジェクトは小さなオブジェクト群から構成されている。そのようなオブジェクト群の格納方法は以下のように2つに分けられる。

  1. 内部収納(internal storage)では、小さなオブジェクトの内容は大きなオブジェクトの内部に格納されている。
  2. 外部収納(external storage)では、小さなオブジェクトは独自の場所に置かれ、大きなオブジェクトはそれへの参照のみを格納する。

内部収納は、参照のための領域や動的メモリアロケーションのためのメタデータを必要とせず、デリファレンスや小さなオブジェクト用のメモリ確保に要する時間も節約でき、効率的である。内部収納は、同種の大きなオブジェクトをメモリ内に連続して配置することで「参照の局所性」を高める効果もある。しかし、外部収納が好まれる状況も以下のようにさまざま存在する。

  • データ構造が再帰的(つまり自身を内包する可能性がある)ならば、内部収納は不可能である。
  • 大きなオブジェクトが限られた領域に格納されている場合(例えばスタック)、オーバーラン(オーバーフロー)を防ぐためにその内容の大部分を外部収納にして物理的なサイズを削減する必要があるかもしれない。
  • 小さなオブジェクトのサイズが可変である場合、それを内部収納すると大きなオブジェクトを可変サイズとする必要が生じ、効率が悪くなることがある。
  • 参照を使うことで仕様変更などに柔軟に対応できる。

Javaなどの言語は内部収納をサポートしない。これらの言語では、全てのオブジェクトが参照を通してアクセスされる。

言語サポート[編集]

アセンブリ言語では、参照はメモリアドレスや配列のインデックスで表現される。これを使うには注意が必要である。メモリアドレスは何を指しているかわからないし、指しているものの大きさも構造も意味もアドレスからはわからない。そのような情報はロジック自体に組み込む。その結果、間違ったプログラムが参照を間違って解釈してエラーが発生し、プログラマは途方にくれることになる。

最初の不透明参照のひとつとして、LISP言語のconsセルがある。これは単純化すれば他の2個のLISPオブジェクトへの参照から構成されるデータ構造であり、他のconsセルへの参照も持つことが出来る。この構造で単純な線形リストを構成することもできるし、「ドットリスト」と呼ばれる二分木を構成することもできる。

他の初期の言語FORTRANは明示的な参照を持っていないが、参照渡しで暗黙のうちにそれを使っている。

C言語ポインタを導入し、これが今日でも参照の中では最も有名である。これはアセンブリ言語のアドレスそのものと似ているが、コンパイル時にその参照が指しているデータのデータ型を指示することで誤解(バグ)が生じるのを防いでいる。しかし、C言語は「弱い型」を採用しているため、型変換(ポインタの型を明示的に他のデータ型に変換すること)によってバグの危険が生じる。その後継とも言えるC++はポインタの型の安全性を強化しようと試みている(新たな型変換演算子の導入、標準ライブラリでのスマートポインタ導入など)が、互換性維持のため意図的にこれらの安全機構を出し抜くこともでき、問題は残っている。なお、C++には、さらに型としても「参照」というものがある(#C++の「参照」を参照)。

こんにちの多くのスクリプティング言語などでは、reference などと称される不透明な参照を採用している。これらの参照は C言語のポインタのようなデータ型であるが、生のアドレスに変換したり危険な変換ができないという点で C言語よりずっと安全になっている。このように「管理された」言語では、参照は実際には指すべきデータへのポインタへのポインタになっていることが多い。C/C++から見れば、これらの言語は二段階ポインタを参照に使っていることになる(典型的な実装としては)。ガベージコレクタだけが不透明性を生む中間のポインタに直接アクセスすることができる。一般にポインタ同士の演算もサポートされていない。

FORTRAN[編集]

FORTRANで参照と言えば、オブジェクトの別名 (alias)を意味することが多い。例えばスカラ変数、配列の行と桁などである。参照をデリファレンスする方法はなく、参照されているものを直接操作するという概念もない。FORTRANの参照は null の場合もある。他の言語のように参照によって線形リスト、キュー、木構造などの動的構造を処理することができる。

Java[編集]

Javaでは、クラス型、インタフェース型、型変数、配列型が、参照型(reference types)である[1]

関数型言語[編集]

C++の「参照」[編集]

関連項目[編集]

[編集]

  1. ^ http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.3