変数と配列

前回は、プログラム内にいろいろな式を書いて、コンピューターに計算させてみました。 今回は、その計算結果をメモしておける「 変数 」という仕組みを使ってみましょう。

- 目次 -

コンピューターに値をメモできる「 メモリー 」

実用の場面では、計算途中の値をメモするものも必要

プログラム内の式では、足し算や引き算、かけ算や割り算、小数点のある数値の計算などができましたね。 これだけでも、自分で電卓をたたく代わりなどに、それなりに便利に使えそうです。 でも、みなさんが電卓を使う場面を思い返してみてください。机の上にあるのは電卓だけでしょうか? 恐らく、なにか値をメモするためのもの(紙など)も必要ではないでしょうか。

たとえば、ある計算を行って、その値をメモして、また別の計算を行って、その結果をメモして、 さらにメモした値を使って計算を行って… などです。よくありますね。 このように、日常的な計算の場面では、計算を行うもの(電卓)と、 計算途中の値をメモしておくもの(紙など)が、大体ワンセットで必要になります。 なので電卓自体に、値をメモしておく機能が付いている製品も数多くあります。

コンピューターにも、値をメモするための部品「 メモリー 」が入っている!

これはプログラムの中でも全く同じで、値をメモしたいという場面はたくさん出てきます。 でも、紙を用意しなくても大丈夫です。 コンピューターの中にはちゃんと、値をメモするための部品である 「 メモリー」 が搭載されています。

メモリーの図

メモリーは英語で「 Memory 」と書き、これは日本語では「 記憶 」という意味です。 つまりメモリーは、メモ内容を紙などに書きだすのではなく、自分自身の中に覚えておく事ができる部品です。 そのため動作が高速で、メモするのも、メモした値を読みだすのも一瞬でできます。

さらにメモリーには、とても多くの値をメモしておく事ができます。 その量は、みなさんがPC( パソコン )を買うとき、カタログに 「 メモリー: 4GB 」 などと書かれているはずです。 「 B(バイト) 」は、値を覚えておく単位のようなものです。「 G(ギガ) 」は 1000 の 1000 倍の 1000倍を意味します。 つまりメモリーは、何十億個以上もの値をメモしておく事ができるのです。

CPUと同様、メモリーも 1 と 0 でやり取りする電子部品

このように高性能なメモリーですが、直接使うのは、人間にとって簡単ではありません。 というのも、第1回の最後で触れた、コンピューターの頭脳であるCPUの話をふり返ってみてください。 CPUはあくまでも電子部品であるため、 人間よりも電子回路や電気信号にとって都合がよいように作られているのでしたね。 プログラムは 1 と 0 を並べて記述する必要がありますし、 計算する数値も、CPU内部ではすべて 1 と 0 の列として扱われています。 そしてメモリーも、CPUとやり取りする電子部品であり、実はやり取りできるのは 1 と 0 の列だけなのです。 当然、記憶できるのも 1 と 0 の列だけ、という事になります。 つまり、もし人間がメモリーを直接使って値をメモしたければ、 まずその値を、何らかの方法で 1 と 0 の列に置きかえる必要があります。

メモリーとCPUが、1 と 0 で通信する図

また、メモリーはたくさんの値を記憶できるわけですが、 「 どの値を、メモリーの中のどこに記憶させるのか 」 を、 無味乾燥な 「 アドレス ( Address、住所という意味 )」という番号 で指示しないといけません。 これもコンピューター内部では 1 と 0 の列で表す必要があります。 そして、このアドレスは値を読みだす際に必要なので、忘れてはいけません。

たとえば、すごく厚いメモ帳があったとしましょう。 その各ページに1つずつ、値をメモして使う場合を考えてみてください。 この例だと、メモリーのアドレスに相当するのは、メモ帳のページ番号です。 何ページ目に何の値をメモしたかを覚えておかないと、メモした値を読み出す時に困りますね。

それと同じで、アドレスを覚えておかないと、メモリー内にたくさん並ぶ値の中で、どれが目的の値かわからなくなります。 ただ、アドレスは無味乾燥な番号なので、人間が覚えておくのは大変です。

アドレスを指定してメモリーの値を読み出す図

メモリーへの値のメモを、簡単にできる「 変数 」

「 変数 」は、アドレスの代わりに名前を付けられ、1 と 0 への変換も気にせず使える

でも、心配しなくても大丈夫です。 私たちがプログラム内に、前回のように人間にわかりやすい形で計算式を書けるのは、 言語処理系というものが、CPUとのやり取りを仲介してくれるからでしたね。 メモリーとのやり取りも、私たちにとって使いやすいように、言語処理系が仲介 してくれます。 そのための便利なしくみが 「 変数 」 です。

変数には、覚えにくいアドレスではなく、自由にわかりやすい名前( 変数名 )をつけて、値をメモできます。 値を読みだすのも、変数名だけわかっていれば大丈夫です。 また、メモする値を、人間が 1 と 0 になおす必要もありません。 そういった、メモリーとのやり取りに必要な作業は、背後で言語処理系が行ってくれます。便利ですね。

言語処理系が、メモリーへのアクセスを仲介する図

実際に、変数を作ってみよう!

それでは、プログラムの中で、変数を用意してみましょう。 例として、整数の値をメモする、 「 memo 」という名前( 変数名 )の変数を作ります。 それには、プログラムに、以下のように記述しましょう:

int memo ;

簡単ですね。ただし変数を作っただけなので、まだ実行しても何も表示されません。 このように変数を作る事を、「 変数を宣言する 」といいます。

さて、上のプログラムで、先頭の 「 int 」 という部分が気になると思います。 これは、メモする値が整数である事を意味しています。 ここで再び、前回も説明した「 データ型 」の登場です。 プログラム内のデータは、 「 整数 」や「 小数(実数) 」、「 文字列 」などの種類に分けて扱われ、 その種類をデータ型と呼ぶのでしたね。 これらのデータ型にも名前があり、プログラム内でそれぞれ「 int 」、「 float 」、「 string 」 と書きます。

基本的なデータ型の図

上のプログラムのように、変数を宣言するときは、どういうデータ型の値をメモするのかを書く必要があります。 そうすると、データ型ごとに適切な方法を言語処理系が選んで、値を自動的に 1 と 0 の列になおして、メモリーに記憶させてくれるのです。

( ※ たとえば文字列の中の文字は、文字コードという対応表のようなものを使って 1 と 0 の列になおされます。 小数は、IEEE754 という規格に従って 1 と 0 の列になおされます。)

変数に値をメモする = 代入する

それでは、先ほど宣言した変数に値をメモし、それを画面に表示してみましょう:

// 整数をメモする変数「 memo 」を宣言
int memo ;

// memo に「 82 」という値をメモ(代入)
memo = 82 ;


// memo にメモしている値を表示
print ( memo ) ;

「 // 」よりも後はコメントであり、プログラム実行の際は無視される事に注意してください。実際に実行すると、以下のように表示されます:

82

このように、print 関数のカッコの中に変数名を書くと、その値を画面に表示してくれます。 さて、上のプログラムにおいて、変数に値をメモしているのは「 memo = 82 ; 」 の行です。 このように、変数に値をメモする事を、正確な言い方では「 代入する 」と言います。

ここで登場している 「 = 」 記号は、「 右の値を左の変数に代入する 」という意味のものであり、「 代入演算子 」と呼ばれます。 算数や数学の「 = 」記号とは少し意味が違うので、注意が必要です。 恐らく、イメージ的には「 ← 」記号などの方が、本来の意味に近いでしょう。 慣れるまでは「 = 」記号を見たら、頭の中で「 ← 」記号に置き換えるのも手かもしれません。

代入演算子のイメージの図

変数の宣言と、値の代入は、以下のように 1 行にまとめて書く事もできます:

// 変数「 memo 」を宣言して「 82 」を代入
int memo = 82 ;


// memo の値を表示
print ( memo ) ;

実行結果は先ほどと同じです。なお、「 = 」記号の右に、計算式を書く事もできます。そうすると、式の計算結果の値が、変数に代入されます。

変数の値を上書きする

変数は、値を代入した後に、何度でも上書きで代入しなおす事ができます:

// 変数「 memo 」を宣言して「 82 」を代入
int memo = 82 ;

// memo の値を表示
println ( memo ) ;

// memo に 100 を代入して値を上書き
memo = 100 ;


// memo の値を表示
println ( memo ) ;

// memo に 12345 を代入して値を上書き
memo = 12345 ;


// memo の値を表示
println ( memo ) ;

実際結果は以下の通りです:

82
100
12345

プログラムの処理は、上の行から下の行へと順番に実行される事を思い出すと、 確かに変数の値が、代入するたびに上書きされていっている事がわかりますね。

変数を使って計算する

変数に代入した値を使って計算を行うには、計算式の中に、数値などを直接書くかわりに変数名を書けば OK です。 そうすると計算結果は、式の中の変数名の部分を、その変数の値で置きかえて計算したものになります:

// 変数「 memo 」を宣言して「 30000 」を代入
int memo = 30000 ;

// memo を使って計算
print ( memo + 2000 ) ;

実際結果は以下の通りです:

32000

ちゃんと、変数「 memo 」の値である 30000 に、2000 が足された結果が得られていますね。

もちろん、変数を使った計算結果を、別の変数に代入する事もできます:

// 変数「 memo 」を宣言して「 30000 」を代入
int memo = 30000 ;

// memo を使った計算結果を「 memo2 」に代入
int memo2 = memo + 2000 ;


// memo2 の値を表示
print ( memo2 ) ;

実行結果は先ほどと同じ「 32000 」です。

別の変数ではなく、変数「 memo 」を使った計算結果を、memo 自身に代入する事もできます:

// 変数「 memo 」を宣言して「 30000 」を代入
int memo = 30000 ;

// memo を使った計算結果を memo 自身に代入
memo = memo + 2000 ;

// memo の値を表示
print ( memo ) ;

これでも同じ結果が得られます:

32000

さて、先ほど『 「 = 」記号の意味は、算数や数学のイコールとは違う 』と説明しましたが、 この「 memo = memo + 2000 ; 」 という行は、まさにそうですね。 数学だと、こんな関係を満たす memo の値は存在しません。 繰り返しになりますが、ここでの「 = 」記号はあくまでも、「 右の値を左の変数に代入するもの 」です。 上の行では、まず「 = 」の右にある式の値が「 32000 」と計算されてから、それがそのまま「 = 」の左の変数「 memo 」に代入されるだけです。 等式を解いてくれるわけではないので、注意が必要です。

データ型の変換

いろいろなデータ型の変数を使ってみよう!

ここまでは、整数のデータ型である int 型を例に使ってきましたが、 小数( 実数 )の float 型や、文字列の string 型でも試してみましょう:

// 整数の変数「 suuji(数字) 」
int suuji = 12345 ;

// 小数(実数)の変数「 ensyuu(円周率) 」
float ensyuu = 3.14 ;

// 文字列の変数「 aisatsu(あいさつ) 」
string aisatsu = "おはよう" ;

// 変数の値を、行ごとに表示
println ( suuji ) ;
println ( ensyuu ) ;
println ( aisatsu ) ;

実行結果は以下の通りです:

12345
3.14
おはよう

それぞれ、ちゃんと値を代入できていますね。

変数に、別のデータ型の値を代入すると、変数のデータ型に変換される。
変数のデータ型は変わらない!

上の内容を読んでいて、恐らく多くの人が、次のような事を思い浮べたのではないでしょうか: 「 整数 ( int型 ) の変数 『 suuji 』 に、『 3.14 』 って代入したら一体どうなるんだろう? 」

その答えは、「 強引に整数になおされてから代入される 」です。試してみましょう:

// 整数の変数「 suuji(数字) 」を宣言
int suuji ;

// suuji に小数(実数)の値を代入
suuji = 3.14 ;


// suuji の値を表示
print ( suuji ) ;

実行結果は以下の通りです:

3

上の通り、「 3.14 」の小数点以下が切り捨てられ、強引に整数に変換されてから、整数の変数「 suuji 」に代入された事がわかります。

このように変数には、宣言したときのデータ型( 上の例では int 型、つまり整数 )の値しか入れられません。 つまり変数のデータ型は、宣言時に一度決めると、ずっと変わらないのです。 このようなルールを「 静的型付け 」と呼びます。 VCSSLは、C言語やC++などと同じく、静的型付けのプログラミング言語です。

一方で、上のような代入を行うと、変数「 suuji 」のデータ型が小数(実数)型に変わるプログラミング言語もあります。 そのようなルールは「 動的型付け 」と呼びます。 静的型付けと動的型付けには、それぞれ有利不利があり、使う人の好みも分かれます。

データ型の変換は、なるべく頑張ってくれるが、無理な場合もある

さて、どうやっても変数のデータ型には変換できない値を代入したら、どうなるでしょうか? たとえば以下のように、整数の変数「 suuji 」に "あいうえお" と代入する場合などです:

// 整数の変数「 suuji(数字) 」を宣言
int suuji ;

// suuji に文字列を代入
suuji = "あいうえお" ;


// suuji の値を表示
print ( suuji ) ;

実行してみましょう:

--------------------------------------------------
RUNTIME ERROR - 実行時エラー / 1

[ LINE - 場所 ] Test : LINE 5 行目付近
[ CODE - 内容 ] suuji = "あいうえお"
[ INFO - 詳細 ] int型へ変換できません。 ( "あいうえお" )

おっと、青い画面になりました。という事は、エラーの発生です。 このように、無理な変換を行うとエラーになり、プログラムの実行が止まってしまいます。

ただし、一見すると常に無理そうな「 文字列を整数に変換 」という処理ですが、一応は頑張って試してくれます。 そして、文字列の中の文字が、すべて数字の場合などは、うまく変換できて代入が成功します。 上のプログラムで "あいうえお" を "12345" と書きかえて実行すると、エラーは発生せず、実行結果もふつうに「 12345 」という表示されます。

式の中などで、変数を一時的に別のデータ型に変換する「 キャスト 」

ところで、変数の値を、一時的に別のデータ型と見なして、式の中などで使いたい場合もあります。 よくある例は、整数の変数同士で割り算を行う場合でしょう。 前回説明した通り、整数同士の割り算では、結果も整数になってしまいます。 小数点のある計算結果を得たければ、分母か分子が小数(実数)である必要があります。

でも、式の中で、整数の変数の名前に小数点を付けても、小数には変換されません。「 そんな名前の変数はありません 」とエラーになるだけです。 そのような変換をするには、式の中で 「 (float)suuji 」のように、変数名の前に、変換したいデータ型をカッコではさんで書けば OK です。 これで、変数「 suuji 」がどのようなデータ型でも、float 型つまり小数になります:

// 整数の変数「 bunbo(分子) 」
int bunsi = 20 ;

// 整数の変数「 bunbo(分母) 」
int bunbo = 8 ;

// そのままの割り算結果を表示
println ( bunsi / bunbo ) ;

// 小数に変換した割り算結果を表示
println ( (float)bunsi / (float)bunbo ) ;

実行結果は以下の通りです:

2
2.5

上の行は、そのまま整数同士の割り算を行った結果で、やはり整数です。 下の行は、変数名に「 (float) 」を付けて小数に変換した値を使って、割り算を行った結果で、小数の結果が得られています。 このようなデータの変換をキャストと呼びます。

配列

いくつもの値をメモしておける「 配列 」

これまで扱った変数は、1 個につき 1 つの値しかメモできません。 でも、プログラムによっては、非常に多くの値をメモしておきたい場合などもあります。 そのような場合、必要な個数だけ変数をいちいち宣言するのは面倒ですし、個数によっては人力では無理でしょう。

そんなときに便利なのが、1 個につき複数の値をメモしておける、「 配列 」 というものです。 配列がメモできる値の数を、その配列の 「 要素数 」 と呼びます。 配列の宣言は、これまでのふつうの変数の宣言と基本は同じで、違うのは 変数名( 配列名 )の後に[ ] 記号で囲って要素数を書くという点です:

// 要素数 100 の配列「 memo 」を宣言
int memo[100] ;

これで要素数が「 100 」、つまり100個の値をメモしておける配列「 memo 」が作れました。 先頭の「 int 」は、データ型が int 型、つまり整数の値をメモする事を意味しています。これはふつうの変数と同じですね。

配列の中の値には、「 インデックス 」という番号が付いています。 配列に値を代入したり、読みだす際には、 配列名の後に [ ] 記号で囲って、インデックスを指定します。 たとえば、上で作った配列において、インデックス [ 1 ] 番と [ 2 ] 番に別の値をメモしてみましょう:

// 要素数 100 の配列「 memo 」を宣言
int memo[100] ;

// インデックス [ 1 ] 番に値を代入
memo[1] = 11111;

// インデックス [ 2 ] 番に値を代入
memo[2] = 22222;

// [1] 番と [2] 番の値を行ごとに表示
println ( memo[1] ) ;
println ( memo[2] ) ;

実行結果は以下の通りです:

11111
22222

1 個の配列に、ちゃんと 2 個の値をメモしておく事ができました。 このように、配列はインデックスを変える事で、あたかも複数の変数を宣言したかのように扱う事ができます。

(重要)配列インデックスの落とし穴!   [ 要素数 ] 番目は存在しない

配列のインデックスには、慣れるまで注意が必要なルールがあります。 それは、インデックスは [ 1 ] ではなく [ 0 ] から割り振られるという事です。 そのため、使えるインデックスの上限は [ 要素数-1 ] 番目であり、[ 要素数 ] 番目は存在しないのです。 たとえば上で宣言した配列 「 moji 」 は要素数が 100 個なので、使えるインデックスは [ 0 ] 番目から [ 99 ] 番目までです。 [ 100 ] 番目を使うとエラーになります。C言語などでもそうなので、注意しましょう。