|
|
3. 変 数
変数は「へんすう」と呼びます。具体的にはデータを格納するためにメモリー上に確保した場所につけた名前のことです。「変数」という呼びかたのとおり、このメモリー上に確保した場所に格納できるデータは変更することができます。なお、変数に対して定数という変更のできないかたちでメモリー上にデータを格納する方法もあります。コンピューターブログラムはデータとそのデータに対して何らかの作業をすることによって成り立っています。変数を理解することは非常に大切なことです。でも心配はしないでください。決して難しいことではありません。
C言語(およびObjective-C言語)では、変数はまず宣言をしてメモリー上に場所を確保して名前を付けてから使わなければなりません。変数を宣言する時には、その変数に格納するデータは数字なのか文字なのか、あるいは他の変数への参照なのかも決めておきます。
int intValue;
この場合は、最初の int でこの変数が整数のデータを取り扱うものであることを表しています。int は integer(整数)
の略です。次の intValue が変数名ですが、これはアルファベットの大文字小文字、0〜9までの数字及びアンダースコア( _ )を自由に組み合わせて任意の名前を付ける事ができます。ただし名前の最初に数字を使うことはできません。また名前の最初にアンダースコアをつけた変数名はコンパイラの開発者によってすでに命名されているケースが多いので使わないことが推奨されています。同じ名前の変数が複数存在すると場合によっては問題を起こす場合があるからです。最後に文の終わり(式の終わり)を意味するセミコロン( ; )を付けます。セミコロンの付け忘れはコーディングミスの中でたぶんトップクラスに入るものです。付け忘れに注意しましょう。
変数の宣言は、その変数の有効範囲(scope : スコープ)の先頭で宣言しなければなりません。
ここまで書くとすでに頭が痛くなってきている方もおられると思います。最近流行の Ruby などでは変数はソースコードのどこで命名しても良く、データの型も決めなくて良く、セミコロンも必要ありません。ただ名前を付ければ良いだけです。しかし残念ながらCおよびObjective-Cではそういうわけにははいきません。でもその変わりにこのように詳しい情報を最初に決めることによって最終的にでき上がるプログラムの実行速度は飛躍的に向上することになっています。少し難しいかもしれませんが、ここはCの学習の踏ん張りどころです。頑張ってみましょう。以下に変数の宣言方法についてリストアップしてみます。
1: データ型を指定しなければならない
2: 変数名はアルファベット・数字・アンダースコアが使える
3: 慣習的に変数名の最初はアルファベットの小文字が使われる
4: 宣言の終わりには必ずセミコロン( ; )をつける
5: 変数の宣言は、例えば関数内で使うものであれば最初のブロック( { )の直後に記述する
なお、2番と3番の変数名に使える文字、およびその文字を使う順序などの命名規則は「関数」の命名にも適用されます。(関数の章で言い忘れていました。)
話ばかりでは難しいのでソースコードで示していきましょう。
まずXcode 3を起動して[ファイル]メニューから[新規ファイル...]を選択します。[プロジェクト内の空のファイル]を選んで[次へ]をクリックしてください。次の画面で[ファイル名:]を local.c にして[保存場所:]で[選択...]をクリックしてあなたのホームフォルダ内の learn-cフォルダを選んで右下の[完了]を押してください。自動的にソースエディタが起動しますので次のようにコーディングしてください。
local.c
コーディングが終ればターミナルを起動します。ターミナルを起動するとはじめはあなたのホームフォルダから始まります。
cd learn-c
learn-cフォルダに移動します。
gcc local.c -o local
local.cファイルをlocalという実行ファイルにコンパイルします。
./local
local を実行します
「変数 a は4096です.」と表示されたと思います。
local.c の4行目でint型変数の a を宣言しましたが、変数の値は指定しませんでした。私の場合は4096と表示されましたが、環境によってどのような値が設定されるかは特定できません。そこでまず値を初期化することにします。local.c に次のように5行目を追加してください。
local.c
gcc local.c -o local
でコンパイルしなおして
./local
で実行します
「変数 a は0です.」と表示されたと思います。
このソースコードの5行目の次(5行目と6行目の間)に
int b;
と新たな変数を宣言しようとするとエラーになります。一度試してみてください。
関数内で使われる変数(これをローカル変数と呼びます)は関数内のすべての実行式が始まる前にまとめて宣言しなければなりません。ソースコードの5行目で a = 0; という変数の宣言以外の式を実行してしまいましたのでそれ以降は変数の宣言ができなくなったのです。では local.c を次のように書き換えてコンパイルしなおして実行してください。
local.c
「変数 a は1です. 変数 b は2です.」と表示されたと思います。このように変数の宣言と初期化(変数に値を与え得る作業)を同時に行う場合はその後も新たな変数の宣言を続けることができます。
ここまでで、みなさんがどうして意味の分からない部分があると思います。それは、
printf("変数 a は%dです. 変数 b は%dです.
");
という printf( )関数の使い方だと思います。今後この「learn C」を進めていく上で printf( )関数は多用していきます。そこでここで簡単な printf( )関数の説明をしておきたいと思います。
printf ( )
printf( )関数は C 言語の仕様の中で決められているものではありませんが、使用頻度が高いために開発環境開発者が標準で搭載しておくことになっている関数の1つです。このように標準で搭載されている便利な関数は printf以外にも多数あります。
printfはコンソール画面(ターミナル画面)に文字列を表示する関数ですが、正確には標準出力先に文字を印字する関数です。実際に簡単な指示によりプリンターに "Hello World !"などと印字することができます。現在ではデフォルトで標準出力先はモニター画面が指定されていますので、結果としてターミナル上に文字列を表示することになります。
さて、気になるのは引数( )の中の使い方だと思います。ここは大きく次の3つにわかれます
1. " と " に囲まれた一般の文字列。これはそのまま表示されます。
2. \ もしくは ¥ ではじまるエスケープシーケンス文字列。この代表が改行を指示する \n だと思います。¥n と記述する方法もありますが、現在Xcodeがデフォルトで採用している文字コードのUTF-8では¥nと入力するとエラーになることがあります。[option]キー + ¥キー で \ をタイプすることができますのでぜひこちらを使用してください。そのほかに覚えておいてもらいたいのが文字列の中で " や ' を使う場合の \" と \' です。このように \" や \' と記述すると文字列の中で " や ' と表示できます。面白いのは \a でしょう。\a で警告音が1回鳴ります、\a を5回記述すると警告音が5回鳴ります。\n や \" や \a を色々と試して遊んでみてください。
エスケープシーケンスはあくまでも一種の文字列です、必ず " と " の中に記述するということに留意してください。
3. 最後に一番頭を悩ませることになると思うのが変換指定子だと思います。
int a = 1;
int b = 2;
printf("変数 a は%dです. 変数 b は%dです.", a, b);
上記の文字列の中での%と次の1文字で表しているものを変換指定もしくは変換指定子と呼びます。変換指定は文字列が終ってからカンマ( , )で区切って列挙されている変数や定数を指定された方法で文字に変換して表示します。上記の例では %d が変換指定子です。それぞれに対応する変数aとbを整数として表示しますが、もともと変数aも変数bも整数を表していますのであまりありがたみはありません。しかし文字列の中でいきなり「a」や「b」と記述してもそのまま「a」「b」と表示されるだけです。
ここで文字を表す char という型の変数を使って実験をしてみましょう。
char.c
この char.c というソースコードファイルをコンパイルして実行すると
「変数 a はコード番号65で文字Aです.」
「変数 b はコード番号66で文字Bです.」
と表示されます。文字はそれぞれに文字という形とそのコード番号(整数)を持っています。変換指定ではこのようにその変数(値)の色々な表現方法を指定することができます。
printf の最後の f はフォーマット(書式)という意味です。書式を指定してプリント(印字)できる関数という意味になります。書式の指定方法にはこのほかにも色々とありますが、今後その都度必要に応じて説明していきたいと思います。
では変数の説明に戻ります。
scope.c
このサンプルコードを実行すると
「main関数の変数aは1です.」
「hoge関数の変数aは2です.」
「main関数の変数aは1です.」
と表示されます。 関数内で宣言された変数のことをローカル変数と呼びます。変数はその宣言された関数が違えば同じ名前の変数であってもお互いに干渉することはなくまったく別の変数として扱われます。このように変数の有効範囲のことをスコープ(scope)といいます。
jido.c
このサンプルコードを実行すると、
「jido関数の変数aは1です.」
「jido関数の変数aは1です.」
「jido関数の変数aは1です.」
と表示されます。
関数内で宣言されたローカル変数は「自動変数」とも呼ばれます。関数が終了すると自動で変数もなくなり値を格納していたメモリー領域も解放されるからです。このサンプルのようにjido( )関数を何度呼び出しても変数aは新たに作られて1という値で初期化されています。
なお14行目の「a += 1;」の「+=」は複合代入演算子と呼ばれ「変数aに1を足した値をもう一度変数aに入れる」という意味になります。同じく「a -= 1;」はaから1を引いた値をもう一度aに入れる、「a *= 2;」は変数aに2をかけた値をもう一度変数aに入れる、「a /= 3」は変数aを3で割った値をもう一度変数aに入れる、という意味になります。わりと便利なので覚えておくと良いでしょう。
static.c
このサンプルコードを実行すると
と表示されます
今回は説明することが盛りだくさんですが、一番はじめに説明しなければならないのが14行目です.
static int a = 0;
sitatic は記憶クラス指定子と呼び、変数の宣言の最初に付加することによって関数が終るたびに値の格納先の変数がなくなる自動変数とは違い、関数終了後も値の格納先の変数が残る「静的変数」と呼ばれるものになります。
最初に0で初期化されていますが、この初期化が行われるのはこの sum( )関数がはじめて呼び出された時に1度だけです。その後は関数が呼び出される度に引数 num で渡される値を加算して関数終了後もその値を保持し続けます。なおこのような静的変数や後ほど説明するグローバル変数などは、そのプログラム自体が終了するときに破棄されメモリー上に確保された場所は解放されます。
最初に0で初期化されていた変数 a は6行目で sum( 1 )と呼び出されることによって、変数 a に1を加えた整数(int)、つまり 0 + 1 = 1 を返して関数を終了します。
7行目では sum( 10 )と呼び出されることによって、変数 a に10を加えた整数、つまり 1 + 10 = 11を呼び出し元に返します。
同じように8行目では111を返すことになり、9行目では1111を返すことになります。
静的変数のこのような動きは理解してもらえたと思います。次に
1. さきほどから出て来ている /* 〜〜のプロトタイプです */とは何か
2. 今回はじめて関数に引数を指定しましたが、その引数の使い方について
3. 変換指定子の %4d とは何か
4. 変換指定元のところに直接関数を書いていることについて
などを説明しなければいけません。
1. は本来、第1回の「はじめに」で説明しておくべきでした。
2. は前回の「関 数」のところで説明しておくべきでした。
3. 4. についてはその都度説明すると申し上げていたのでまぁ良いだろうと思います。ではそれぞれ説明していきます。
コメント
/* sum関数のプロトタイプです */
のように /* と */ に囲まれた文字列はコメントと呼ばれます。このコメントは中の文字列だけでなく最初の /* と最後の */ も含めてコンパイル時には一切無視されます。ようするに無いものと判断されます。このようにコメントはプログラマが自分のソースコードを後から見直す時に、あるいは他人に使ってもらう時にソースコードの意味をわかりやすくするために書いておくものです。
/* */ は複数行に渡って使えます。
/***************************
static.c
***************************/
のような使われ方をするのを見かけたりもします。またコメントにはもう1つの書き方があります
int sum( int num );
// sum関数のプロトタイプです
と書くと。// 以降はその行の最後までコメントとみなされます。これは一行コメントと呼ばれるもので、その行だけで有効です。もしこれを複数行にわたって使いたければ
//***************************
// static.c
//***************************
のようにする手もあります。
引 数
sum( )関数の宣言は、
int sum( int num )となっています。この ( ) で囲まれた int num のことを引数と呼びます。引数は、その関数が仕事をする際に使用されます。この sum( )関数では合計に加算される整数を引数として渡しています。
関数の宣言では
int sum( int num ) と、
[ 戻り値の型 ] [ 関数名 ( 引数の型 引数名 ) ]
という形で記述します。引数は、( int a, int b, char c ) とカンマで区切っていくつでも宣言していくことができます。このように引数の宣言にはデータの型とその引数の名前が必要となります。
これに対して関数の呼び出し側では static.c の6行目から9行目のように、
 関数名 ( 実データ ) という形で呼び出します。このように関数の呼び出し側の引数には実データを記述しますので 実引数 と呼ばれ、宣言側の引数はまだ値が決まっていないので 仮引数 と呼ばれます。なおこの実データとは数値などの定数はもちろんのことある値を表している変数も実データということになります。
実データを引数として受け取った関数側では、その引数を宣言なしで変数として使えます。
static.c 15行目
a += num;
なお、引数についてとても大事なことがあります。
それは関数の呼び出しのときに実引数として記述した値が直接相手の関数に渡されるのではなく、その実引数の コピー が相手の関数に渡されるということです。つまりこの段階で新しいメモリー領域に値がコピーされます。そして関数に渡されたデータをどのように変更しても元のデータが変更されることはありません。この2つはすでにまったく別のメモリー領域に格納されている別々のデータだからです。
printf( )関数の変換指定
static.c 6行目
printf ( "合計は%4dです.\n", sum( 1 ) );
%d は後から指定されるデータを十進数整数の文字列に変換する変換指定子です。d は decimal (十進数) の略です。% と d の間に4などと数字を入れることによってその十進数に変換される文字列を4桁で表示することができます。試しに static.c の6〜9行目を
printf ( "合計は%dです.\n", sum( 1 ) );
printf ( "合計は%dです.\n", sum( 1 ) );
printf ( "合計は%dです.\n", sum( 1 ) );
printf ( "合計は%dです.\n", sum( 1 ) );
と % と d の間の4を抜いてコンパイルしてみてください。
桁を揃えずに表示されてしまいます。
同じく、
static.c 6行目
printf ( "合計は%4dです.\n", sum( 1 ) );
の変換元のデータに直接 sum( 1 ) と関数を書いています。この sum( )関数は戻り値として整数( int )を返すことになっていますので、この記述法で変換元のデータを指定したことになります。なお sum( 1 ) の後にセミコロン( ; )を入れたくなるところですが、この文( 式 )の終わりは次の ) の後です。このように式の最後にセミコロンを入れることに注意しましょう。
グローバル変数
今までに紹介した変数はすべて関数内で使われるローカル変数と呼ばれるものでした。言い換えれば関数内でしかその変数にアクセスできませんでした。これとは逆にすべての関数からアクセスできる(値を参照したり変更したりできる)変数のことをグローバル変数と呼びます。
global.c
ターミナルで learn-c ディレクトリ(フォルダ)に移動し
gcc global.c -o global
とコンパイルし、./global でプログラムを実行します。
と表示されます。
main( )関数の8行目で
a = 1;
と宣言もされていない変数をいきなり初期化しています。これはプログラムの3行目で宣言されているどの関数からでもアクセスできるグローバル変数aに1という値を与えているのです。そして
変数aは1です.
と無事表示されています。続けて sub( )関数を呼び出して、その sub( )関数内でグローバル変数と同名の関数内変数(ローカル変数)を宣言して2で初期化しています。結果は
変数aは2です.
と表示されます。そして再び main( )関数に戻り再度変数aの値を表示しますが
変数aは1です.
と表示されます。これは sub( )関数内ではグローバル変数と同名のローカル変数aが存在しているためにグローバル変数が見えなくなっていて(隠れてしまっていて)アクセスできなくなっているからです。このように関数内にグローバル変数と同名のローカル変数がある場合はローカル変数が優先されます。sub( )関数内の17行目で2で初期化しているaという変数はあくまでも sub( )関数内のローカル変数のaです。
再度 main( )関数に戻って表示される a 変数はグローバル変数のaです。sub( )関数からではグローバル変数の値の変更もできませんので
変数aは1です.
と表示されます。
アドレス変数 ポインター
引数のところで説明しましたが、引数として渡したデータは実際には新たにコピーされて相手の関数に渡されます。このことは大きく2つの不利な点があります。
1. 新たにメモリー領域が確保されるので多量のメモリーが必要になる可能性がある。大きなムービーファイルを表す変数をコピーすることを考えるとこのことの重大さが分かると思います
2. 引数を受け取った側の関数ではその引数データにいくら加工・変更を行っても肝心の元のデータに加工・変更が行われるわけではない。その結果として [戻り値] という形で加工・変更したデータを呼び出し元の関数に返してやらなければならない
そこでC言語からは ポインター と呼ばれる変数が採用されるようになりました。これは今までの変数がデータそのものを表していたのに対して、ポインターはその変数が確保されているメモリー上の場所(アドレス)を格納する変数です。巨大なムービーファイルを格納している変数は何メガバイトのメモリーを使用しているか分かりません。しかしそのムービーファイルを格納しているメモリーのアドレスはわずか4バイトで表すことができます。そこで関数に引数を渡す場合にこのポインターという実データのアドレスの入っている変数を渡すようにしたのです。この場合にも4バイトという新たなメモリー領域が必要にはなりますが何メガバイトというデータを新たにコピーすることに比べると雲泥の差があります。
またポインター変数を受け取った関数側でもそのポインターが表しているメモリー上のアドレスをたどってそこに存在するデータを直接加工・変更することができるようになりました。巨大な戻り値を返す必要もなくなったわけです。
(加工が成功したことを YES NO で報告する戻り値や新たにつくられたアドレス(ポインター)を返す戻り値は今でも多用されています)
ポインター変数の宣言は、通常の変数宣言の変数名の前 (あるいは型名の後) にアスタリスク( * )を付けます
int *a;
もしくは
int* a;
と宣言します。
お疲れさまでした。これで第3回は終ります。次回は条件分岐について掲載したいと思います。合せて switch文の説明もしたいと思います。そして第4回ではループ文の説明に入っていきます。条件分岐とループ文が使えるようになればターミナル上でもあなたの指示に反応するプログラムが作れるようになります。そしてプログラミングもずいぶんと楽に感じるようなると思います。がんばりましょう。
|
|
|