配列はベクトルや行列のようなものです。指定した数だけの変数を一度に作ることができます。
int a[200];
とすれば、a[0]
から a[199]
まで、200個の変数になり、それぞれ要素といいます。これは
int a0, a1, a2, ..., a199;
と書くのとは当然意味が異なります。どちらも同じ200個の変数はできますが、配列にしておくと、別の整数変数(int) i
を使って a[i]
と i
番目の変数を指定できるのですが、後者ではできません。そもそもにして、200個並べて宣言するのが面倒です。
さらに、int a[5][200]
と書くと、「要素200個の配列」が5個、の配列になります。
a[0][0] ... a[0][199]
. .
. ... .
. .
a[4][0] ... a[4][199]
さて、ここで注意してほしいことは、配列の添字は0から始まり、行や列の数-1までということです。我々が日ごろ親しんでいる行列やベクトルの成分の呼び方とちがう(1ずれる)ことに注意してください。
では、int a[200];
と宣言してあるところで、201個目の a[200]
をつかうとどうなるでしょう。コンパイル時にはエラーは出ず、一見動くのですが、実際には他の変数の内容を壊していることがよくあります。これはC言語では様々な理由により、配列の要素数はチェックしておらず、また、変数はコンピュータ内部で並べておかれていることが多いためです。もっと離れた a[10000]
などを書き込むと、Segmentation Fault というエラーを出して止ったり、しばらく動いた後で全く別の部分で突然誤動作したりします。そのため、配列の要素数には厳重に注意を払わなければなりません。
手っ取り早いのは「十分に多く確保すること」ですが、必要以上に取り過ぎるとは、同じコンピュータを共有する他のプログラム、つまり他の人の迷惑になるので、必要最低+αくらいにしておきましょう。
C言語の最も大事な概念の1つにポインタがあります。ポインタとは一言でいうと、「変数がしまってある場所」を指し示すものです。
コンピュータにはデータを記憶しておくためのメモリがあります。メモリには、順番に番地(アドレス)がふってあります。演習用のコンピュータの場合、int
変数一つにつき、4つのアドレス分(4 バイト)を使用します。float
も4、double
は8バイトを使います。変数の値は全て、このメモリの中におかれています。
ポインタは変数の置かれたアドレスを指し示すものです。アドレスは0から順にふってある整数で、それを指すポインタも実体は整数値です。ただ、その数値を直接使うことはなく、その数値の指し示した場所にあるものを操作するためのものです。
アドレス | メモリの内容 | |||||||||||||||
0 | ||||||||||||||||
16 | ||||||||||||||||
32 | int a | float b | ||||||||||||||
48 | double c | |||||||||||||||
: |
(int a
のポインタの値は32, b
のポインタの値は36、c
のポインタの値は56)
このポインタはなんのためにあるか、という疑問に対して、ここでは答を1つ書いておきます。処理に使うための、数千という膨大なデータがあったとします。当然このデータはメモリに記憶されています。これを別の処理(関数)渡したいときにどうするかというと、1つにはこの値を全部引き渡す、という方法があります。つまり、コピーを渡すわけです。それに対して、「◯◯番地に書いてある」とだけ伝えることも可能なわけです。数千のデータをコピーするには時間も場所も必要ですが、番地を教えてその場所にある値を使いなさいと依頼するのは一瞬で済みます。このような場合に、ポインタを使います。
他の例として、文字変数 char
を幾つか連ねて表す文字列は、一般にcharのポインタで表します。これも、全部の文字を渡すのではなく、文字列の書いてある場所を伝えるだけで済むというメリットのためです。
このポインタというものは理解すれば便利なものなのですが、慣れるまでは時間がかかります。ですから、今後C言語を使う機会がありそうな人は、じっくりと時間をかけて理解してください。いずれ必要になる日が来ます。ポインタはC言語を極める第一歩ですから。
幸いにして(?)、本演習の課題には、ポインタそのものの概念が必要となることは、ほとんどなさそうです。最低限、文字列を扱うときにchar *strを使うことと、scanf
等で文字列を読み込む場合にはchar str[200];
のような配列を使うことを覚えておいて下さい。
Cでは、変数の値(内容)だけでなく、 その変数のアドレス(番地)を参照することもできます。 このアドレスをしまっておく変数をポインタと呼びます。
ポインタを使う際は、次のようにします。
&a
で参照できる。
一般に変数を使う場合は、
名前とサイズを宣言して、値を入れておける「箱」を用意します。
その「箱」の中身(値)を知りたい時は、その変数名を指定すれば
わかります。一方、その「箱」がどこに置いてあるのかは、
その変数名の前に &
をつけて指定します。
この置き場所(アドレス)は、そのときの計算機環境によって変わるものです。
int *b;
のように行う。
b = &a;
のように行う。
*b
で参照する。
他の「箱」の置き場所(の値)を入れておく「箱」を
用意することも可能です。
これが、ポインタ変数です。
ポインタ変数は、その変数名だけを指定したときは「置き場所」を
示す値(アドレス)が返ってきます。また、そこの場所の箱の中身を知りたい時には
その変数の前に *
をつけて指定します。
ポインタは、主に次のような場合に使います。
特に、サブルーチン(関数)において、 文字列(文字型変数の配列)の参照に用いられます。 文字列の最後はヌルで判定できるため、 先頭のアドレスが分かっていれば 文字列同士を比較したり、コピーしたりといったことが、 ポインタ変数を使って簡潔に記述できます。
関数から返すことのできる値は、普通はひとつ(関数の値)ですが、 返す値を書き込む場所(アドレス)を引数として渡せば、 いくつでも値を返すことができます。 一般に入力関数は引数として、入力された値を入れるための アドレスを渡してやる必要があります (第3節参照)。
アドレス変数を使う場合、次の事に注意して下さい。
たとえばchar *message;
とだけ宣言して、後からmessage = "hellow !"
などと代入することはできません。 (すでに定義してある文字列の先頭アドレスを代入することができます。)
例えば配列のデータを参照するのに使用している場合、 添字の上限を超えてしまうことがあるので、 十分注意してください。 文字列であれば、その値(ヌル)で配列の最後を判別できます。