個人的備忘録
配列型を理解するために必要そうな定義
[訂正] アドレス演算子の項目で、間違った記載があったので定義以外の部分を削除。
------ 補足 ------
参照元の略称一覧
[C99] JIS X3010、ISO/IEC 9899:1999
※ページ数は、私の便宜上 JIS X3010本来のページ数ではなくPDFのページ数
[ANSI] 新ANSI C言語辞典 平林雅英 著
[FAQ] C FAQ ( http://www.kouno.jp/home/c_faq/ )
[ポ完] C言語ポインタ完全制覇 前橋和弥 著
[法大] 法政大学奥山研究室 ( http://okuyama.mt.tama.hosei.ac.jp/unix/C/ )
※なるべく日本語翻訳は、JIS X3010の翻訳を記載。
(ANSI を参考に分かりやすい言い回しに変えているものもある。英文は、ISO/IEC 9899:1999の原文のまま。)
※「pointer」「points」「returns the address」に気をつける。
※「ポインタ」は「変数」「型」「値」の意味がある。
※「 [] 」には、「配列宣言子」と「添字演算子」の2つがある。
------------------
[型] (C99 6.2.5 (p28) 型、ANSI p285)
構文:基本型|派生型
派生型は、オブジェクト型、関数型及び不完全型から幾つでも構成することができる。
※派生型(derivation type)を構築するこれらの方法は、再帰的に適用できる。(C99 6.3.5 (p31))
派生型は、
・配列型(array type)
・構造体型(structure type)
・共用体型(union type)
・関数型(function type)
・ポインタ型(pointer type) がある
注意:「〜型」という表現は「〜型指示子の宣言による型」とか「〜型名」という意味でも使われる。(ANSI p286)
[配列型(array type)]) (C99 6.2.5 (p30) 型-派生型-配列型)
配列型は、要素数(サイズ)と要素の型(要素型、element type)の2つで特徴化されるデータ型。
すなわち、「要素型をもつオブジェクトが連続してメモリに記憶されるデータ」という意味。(法大 10.1)
配列型の要素型は型指定子で指定し、要素数(サイズ)は宣言子(Declarators)の配列宣言子(Array Declarators)を用いて指定。
配列型は要素型からの派生型であり、構造体型と同じように「集成体型」(集合体型,aggregate type)に属する。
集成体型の初期化には,波括弧で囲んだ初期化子を使う。[C99 6.7.8,16]
更に下記が加わる。(C99 6.3.2.1 (p38) 左辺値、配列及び関数指示子)
式中の「Tの配列(array of type)」という左辺値は、3つの例外を除いて「Tへのポインタ」(pointer to type) の式に型変換(converted to an expression with type)し、
それは配列オブジェクトの先頭の要素を指し(pointer that points to the initial element)左辺値ではない。(C99 6.3.2.1,3)
また、register記憶域クラスを持つ場合、動作は未定義。
例外1)sizeof演算子のオペランド → 派生元の型の大きさに、配列の要素数を掛けた大きさ
例外2)単項&演算子のオペランド → 配列へのポインタ(配列全体へのポインタ)になる
例外3)文字列配列を初期化するのに使われる文字列リテラル → 初期化(C99 6.7.8,14 (p99))
※配列オブジェクトの先頭の要素(配列の先頭要素)とは、「第1要素のアドレス」(address of the initial element)という意味。(法大 10.1)
2次元配列の場合の「配列の先頭要素を指す」は「先頭要素へのポインタを指す」になることに注意。
(他に、ANSI p279 オブジェクト指名子、ポ完 p59、FAQ 6.3, 6.12 も参照)
[配列型派生(array type derivation)](参照:新ANSI C言語辞典 p392)
要素型から配列型を構築すること
[要素型(element type)] (ANSI p446)
配列の1つの構成要素のオブジェクトの型。
ex.)「charの配列」を配列型派生(array type derivation)
→要素型がchar型の配列型(array of char(要素型はchar)
ex.)「要素型がchar型の配列型(array of char (要素型はchar))」を配列型派生(array type derivation)
→要素型がchar型の配列型の配列型(array of array of char(要素型はarrof char))
[ポインタ型(pointer type)](C99 6.2.5 (p31) 型)
被参照型(referenced type)と呼ぶ関数型、オブジェクト型又は不完全型から派生することができる。
被参照型の実体を参照するための値をもつオブジェクトを表す。
被参照型Tから派生されるポインタ型は、「Tへのポインタ」と呼ぶ。
被参照型からポインタ型を構成することを「ポインタ型派生(pointer type derivation)」と呼ぶ。
[ポインタ型派生(pointer type derivation)](C99 6.2.5 (p31) 型、ANSI p424)
被参照型からポインタ型を構成すること。
[被参照型(referenced type)] (ANSI p398)
構文:被参照型: =関数型|オブジェクト型|不完全型
解説:ポインタが参照する実体の型
[宣言子(Declarators)]
型指定子にタグ、列挙体をメンバを含まない場合には,次のいずれかの宣言子を付す必要があります。[C99 6.7.2 (p76) 型指定子]
・識別子(identifier)
・ポインタ宣言子(Pointer Declarators)
・配列宣言子(Array Declarators)
・関数宣言子(Function Declarators)
識別子自体も又,宣言子です。ポインタ宣言子はポインタ型,配列宣言子は配列型,関数宣言子は関数型のデータを扱うための宣言子です。
[配列宣言子 [] (Array Declarators)] (C99 6.7.5.2 (p90) 配列宣言子)
ex.) char x[5]
要素型は、char で配列宣言子は、x[5]
この結果、要素数5の「charの配列」(array of char、char型配列)が出来る。
個別要素は、x[0]から x[4]までの5個であり、すべてchar型のオブジェクト
[添字演算子 [] ] (C99 6.5.2.1 (p55) 配列の添字付け)
制約:式の一方は、「オブジェクト型〜型へのポインタ (pointer to object type)」をもたなければならない。
もう一方の式は、整数型をもたなければならない。結果は「〜型 (type)」をもつ。
意味規則:E1[E2]が (*((E1)+(E2)))と等価であると定義する。
ex.) array[0][1] → (*((array)+(0)))[1] → (*((*((array)+(0)))+(1))) → (*(*(array+0))+1)
[間接参照演算子 * (Indirection Operator) ] (C99 6.5.3.2 (p61) アドレス及び間接演算子、ANSI p304 間接参照演算子)
制約:単項*演算子のオペランドは、ポインタ型をもたなければならない。
意味規則:単項*演算子は、間接参照を表す。(間接参照とは、ポインタを介して間接的に値を参照すること。一重間接(*)二重間接(**)可能(ANSI p304 間接参照))
・オペランドがオブジェクトを指している場合、その結果はそのオブジェクトを指し示す左辺値とする。
・オペランドが「〜型へのポインタ(pointer to type」をもつ場合、その結果は「〜型(type)」
・正しくない値がポインタに代入されている場合は、動作は未定義。
[アドレス演算子 & ] (C99 6.5.3.2 (p61) アドレス及び間接演算子)
制約:単項&演算子のオペランドは、関数指示子、[]演算子若しくは単項*演算子の結果、又は左辺値でなければならない。
・左辺値の場合、ビットフィールドでもなく、register記憶域クラス指定子付きで宣言されてもいないオブジェクトを指し示さなければならない。
意味規則:そのオペランドのアドレスを返す。(The unary & operator returns the address of its operand.)
結果の型:&op1 → op1の型へのポインタ型
演算:アドレスを求める。(絶対的なものか相対的なものかは処理系依存)
演算結果:&op1 → op1(が示すオブジェクト(変数)や関数)のアドレス(lvalue(オブジェクト指名子)ではない。)
※注意:アドレスを返すだけで、 &演算子はポインタではない。ただし、結果の型はポインタ型をもつ。(ANSI p269)
・オペランドが [〜型] の場合結果は、 [〜型へのポインタ] をもつ。
・オペランドが、単項*演算子の結果の場合、*演算子も&演算子も評価せず、両演算子とも取り除いた場合と同じ結果となる。
ただし、その場合でも演算子に対する制約を適用し、結果は左辺値にならない。
・オペランドが[]演算子の結果の場合、単項&演算子と、[]演算子が暗黙に意味する単項*演算子は評価されず、
&演算子を削除し[]演算子を+演算子に変更した場合と同じ結果となる。
・これら以外の場合、結果はそのオペランドが指し示すオブジェクト又は関数へのポインタとなる。
[ポインタ演算] (C99 6.5.6 (p65) 加減演算子、ANSI p284 加算演算子、ポ完 p42)
・配列の要素でないオブジェクトへのポインタは、要素型としてそのオブジェクトの型をもつ長さ1の配列の最初の要素へのポインタと同じ動作をする。
・整数型をもつ式をポインタに加算又はポインタから減算する場合、結果は、ポインタオペランドの型をもつ。
汎整数型の式「N」が値「n」をもち、ポインタ型の式「P」が配列オブジェクトの第i要素を指すなら、
式「(P)+N」(又はN+(P))は、配列オブジェクトの第i+n要素を指す。((P)+Nは、「Pのアドレス+n×オブジェクトの大きさ」のアドレスを指す。)
式「P」が最後の要素を指していたら、「(P)+1」は最後を1つ超えた要素を指すが、この要素の先頭の1バイトの存在は保証されている。
ただし、最後を1つ超えた要素に*演算子を適応した場合の動作は、未定義。(ANSI p284)
[配列の関数の仮引数の宣言] (ポ完 p186)
関数の仮引数の宣言では、配列は要素数が無視されたポインタ(〜型へのポインタ型)に読み替えられる。
これは「char a[] 」と「char *a」が同じ意味になる唯一のケース。
よって、 *array == array[] == array[5] で、全て *array となる。
ex.)「void Func(char array[5])」の「array[5]」は配列ではなく、char *array になっている。
ex.)「void Func(char a[][5])」は「void Func(char (*a)[5] (配列へのポインタ(charの配列(要素数5)へのポインタ)」に読み替えられる。
※注意:けして、void Func(char **a) ポインタへのポインタではない。(FAQ 6.18)
[空の[]が書ける場合](ポ完 p189)
関数の仮引数の宣言、初期化子により配列のサイズが確定出来る場合、グローバル変数をextern宣言する場合
C言語 配列 (用語)
コメントはまだありません。