たのしい工学

プログラミングを学んで、モノをつくりたいひと、効率的に仕事をしたい人のための硬派なブログになりました

【独学するコンピュータサイエンス】配列とポインタ

   

配列の概念

以前説明した様に、配列とはある特定の型のデータがあるアドレスから連続して配置されている状態を指します。文字列は先頭アドレスから連続して char 型の値が配置されています。連続といっても隙間無くその型のデータで埋まっているとは限りません。隙間のある構造でもその型のポインターで操作する分には何の問題もありません。しかし異なる型のポインターで操作する場合が問題です。
これはアラインメントやパディングが関係します。アラインメントとはデータをアドレスの整数倍で位置を合わせる事で、パディングとは型の内部表現の詰め物です。パディングは存在しない場合もありますし、存在する場合その部分に何が入るかは不定です。どちらも CPU 等環境の都合によるものです。今までこの部分は無視してきましたので少し詳しく説明します。
変数の型の大きさとは、変数を格納するのに十分な領域とその隙間を合わせたものなのです。配列は特定の型の大きさを連続して取りますから隙間も含まれます。
「short 型の大きさが内部では 4 バイトでも short のデータそのものは 2 バイトしか使用せず、残り 2 バイトは隙間として存在している場合」を例にします。short 型へのポインターでアドレスを操作してもきちんと 4 バイトずつ移動しますしその内容を読み書きしても short 型の有効データである 2 バイトだけが対象となり、残りの隙間の分は無視されます。これを char 型へのポインターで操作してみましょう。char 型は 1 バイトで隙間は無いと決められています。すると char * 型でその short 型領域を読み書きすると隙間部分も読み書きが出来てしまいます。それを防ぐ為作成した型で読み書きするのは配列の基本です。
少々込み入った話も出て来ましたが、ある特定の型が連続したアドレスに配置された状態を配列と呼び、配列の読み書きはその配列の型で行う、と覚えていただければ十分です。

配列はポインタそのものである

配列の実態は一言で済ませるとポインターそのものです。配列の要素への読み書きの方法もポインターそのものです。

a1[ 3 ] = 20;

int 型の一次元配列変数 a1 の要素番号 3 に対して int 型の 20 を書き込んでいます。この動作は *( a1 + 3 ) = 20; と同じ

配列の正体は読み書き可能な領域の確保と開放を自動的に行い、その領域のアドレスの内容を読み書きするポインターといえます。つまりポインターに malloc と free に相当する機能を持たせ、その領域のアドレスの内容を操作する事に特化し、対象アドレスはその領域の先頭アドレスからの相対位置で求めるポインターです。
ポインターに機能を追加して配列という新たな枠組みにはめ込んだかの様な設計になっている為、 C 言語の配列は約束事が多く複雑なのです。

配列の宣言と中括弧

本来は宣言と同時に初期化を記述する場合は配列に限らず中括弧が必要なのですが、単一の場合や文字列リテラルの時は省略が可能です。即ち int i = 5; は int i = { 5 }; と同じ事です。単一の値の初期化の場合は省略可能というより付けても構わない、の方が正確です。

配列の添字演算子の正確な定義

複雑になるので敢えて説明を後回しにした点があります。
配列の宣言と使用法は次の様になります。

int a[ 10 ];
a[ 2 ] = 3;
ここで a[ 2 ] を E1[ E2 ] と置きます。規格では E1 又は E2 のどちらか片方がオブジェクトへのポインターであり、もう片方は整数型であると定義されています。
配列型はある例外を除き、式の演算に先立ち「~型の配列」から「~型へのポインター」へ型変換されます。配列宣言子を用いて宣言した識別子は配列型を持ちますから、添字演算子による演算前にポインターに自動変換されます。従って配列に対して添字演算子が使用可能になります。
となると添字演算子は配列型に限らずポインターであっても使用可能です。
規格ではポインターと整数の位置は定義しておらず、 E1[ E2 ] は ( *( ( E1 ) + ( E2 ) ) と同じであるとしています。配列はポインターの特殊型というのがお分かりいただけると思います。
この規格の記述の裏を返すと E1[ E2 ] は E2[ E1 ] と記述しても同じになります。

配列はポインターと似ていますが型は異なります。ある例外を除き演算に先立ち配列がポインターへと自動型変換されるだけです。ここは間違わないでください。

配列と関数

C 言語では配列を関数に直接渡す事は出来ません。ポインターを用います。
プロトタイプ宣言に配列を記述すると、一次元目の要素数は無視され尚且つポインターとして扱われると説明しました。これは sizeof 演算子を用いると簡単に確認出来ます。

char型とstring型

文字はChar型で文字列リテラルはchar型の配列

つづきはまた次回です!

 - コンピュータサイエンス