プリント基板設計・シミュレーション

TOP > アポロレポート > コラム > 第81回 プログラミングについて『いまさらですがC言語の門を叩こう その4~変数~』
コラム
2024/09/20

第81回 プログラミングについて『いまさらですがC言語の門を叩こう その4~変数~』

アポロレポート

 だんだんと話しが難しくなってきますが頑張って下さい。今回は変数についての話しです。前回は定数の話しをしましたが、定数があるのであれば変数があることについては疑問はないと思います。
 変数とは読んで字のごとく値の変わる数です。コンピュータ上ではこの変数がどのようになっているのかというと、前回の文字列定数のことを思い出してみて下さい。文字列定数はメモリー上に固定の文字列があってその番地が値でした。変数は(配列ではないもの)もこれと同じ様にメモリー上に可変の値があって、その値が変数の値そのものになります。

 C言語の変数は基本的には算術上の変数しかありません。文字や文字列専用の変数はないのです。不思議に思われる方もいると思いますが、これがC言語の特徴の1つなのです。文字や文字列も算術上の変数を代用することで、C言語自体を小さくまとめることができたといっても過言ではありません。

 C言語で扱える整数の変数には、1バイト、2バイト、4バイト、8バイト(処理系によります)のバイト数を使うものがあります。そして、それぞれに符号あり、符号なしの型があります。
 1バイトとは、コンピュータ上での値の最小単位であるビット(0と1の値しか持たない)が8つ集まったものです。昔は6ビットで1バイトというコンピュータもありましたが、確かIBMが8ビットで1バイトというコンピュータを作ってからそれが当たり前のようになって現在に至っています。
 1ビットで表現できる数は、0と1だけです。2ビットだと、0~3までが表現できます。ということは、8ビットになると0~255までの値を表現することができるということになります。これでは負の値が表現できないので、通常最上位のビットを符号用のビットとし、ー128~127までの値を表現することにしています。同様に2バイトは16ビットで表現できる値も大きくなります。
 これらをC言語では次の様に表現します。

 char      符号ありの1バイトの整数の変数

 unsigned char  符号なしの1バイトの整数の変数

 short      符号ありの2バイトの整数の変数

 unsigned short 符号なしの2バイトの整数の変数

 int       符号ありの2または4バイトの整数の変数

 unsigned int  符号なしの2または4バイトの整数の変数

 long      符号ありの4または8バイトの整数の変数

 unsigned long  符号なしの4または8バイトの整数の変数

int 型と long 型については処理系によって異なります。例えば、64ビットのコンピュータであるDEC社のAXPのものでは int が4バイトで、long が8バイトです。
 しかしながら、現在普及しているWindows95のパソコンでは int 型も long 型も4バイトです。
 これらの変数はバイト数が違うので表現できる値の範囲が違っていても同じ性格の変数です。この中で1バイトの変数が文字や文字列を表現するときに都合がいいということで、それらの目的にも使用されるので char となっている様です。もし文字専用の型があったら char ではなく byte となっていたかも知れません。


 変数は次の様に定義します。

 int i;

この様に定義すると、コンパイラは機械語に翻訳するときにメモリー上に4バイトを確保する命令にするのです。そして、そのメモリー上の4バイトを整数の変数 i 専用に使用するのです。

□ □ □ □ □ □ □ □ □ □ □ □ □
          |-4バイト-|
          |
         500番地

メモリー上にその4バイトが上の図のように確保されたとします。このとき変数 i のアドレスは500番地で、値は0か不定のどちらかです。変数の初期値については後で説明します。
 プログラムの実行時には決して変数 i というものがあるのではないことに注意して下さい。変数 i というのはあくまでもC言語上でのことで、人間が理解しやすいように便宜的に付けただけの名前なのです。
 変数の宣言の方法は int 以外の型でも同じですので、

 char c;
 unsigned short s;

などとすれば定義することができます。

 ここで、各変数の定義の終わりにセミコロンが付けてあるのはC言語での1つの文の終わりを示す記号です。C言語は基本的にはセミコロン記号で1つの文の終わりを示すので、複数行にまたがるときには、

 char
  c;
 unsigned
  short
     s
     ;

と書いても同じ意味になります。FORTRANなどでは1つの文は1行に記述することが基本ですので行の終わりを示す記号はなく、逆に次の行に継続するときに継続記号を使用します。どちらがいいのかは断言することはできません。あくまでも慣れの問題のような気もします。
 またC言語は1つの文の終了をセミコロンで示すので、1つの行に複数の文を記述することも可能です。

 char c; unsigned short s;

と書いても同じ意味になります。どのように記述するかは好みの問題かもしれません。

 実数の変数は4バイト、8バイト、16バイト(処理系に依ります)のバイト数を使うものがありますが、整数のような符号なしの実数はありません。実数はコンピュータ内では仮数部と指数部の2つの部分で構成されていますので、4バイトの実数であっても4バイトの整数と同じ値を表現できるとは限りません。例えば大きな数値では、100000000000000000000.0 という値は表現できますが、12345678901234567890.0という値は表現することができません。4バイトの実数の有効桁数は、たかだか8桁なのです。

 float    4バイトの実数の変数     (短精度)
 double   8バイトの実数の変数     (倍精度)
 long double 8または16バイトの実数の変数(倍精度または4倍精度)

変数の定義の方法は整数の変数と変わりはありません。

 float f;
 double d;

とすれば整数の変数と同じように実行時にメモリーが確保されます。

 文字についてちょっと考えてみましょう。通常アスキー文字と呼ばれている文字は、アスキーコード表を見てもらえば分かると思いますが、1バイトで表現できるように定義されています。ですので、文字は通常 char 型の変数に代入したりしますが、short 型でも int 型でも構いません。ただ1バイトで事足りるのに2バイトも4バイトも余計にメモリーを使いますので特別な事情が無い限り特策ではないと思います。

 C言語で使用できる基本的な変数の型はこれだけです。なんだかすごく寂しい気もしますが、言ってしまえばこれだけあればなんでもできるということなのです。さて、次は配列について考えてみましょう。配列だからと言って特別な型がある訳ではありません。上で説明した基本の型の変数がたくさん連続して並んだのが配列なのです。配列は次のように定義します。

 short i[10];

この様に定義すると、short 型の変数 i は10個の連続した要素を持つ変数という意味になります。

 short j[10][5];

とすると、short 型の変数 j は連続した5個の要素を10個連続して持つ変数という意味になります。配列の次数は任意です。

 図にしてみると short i[10]; は次のようになります。

     □ □ □ □ □ □ □ □ □ □ ... □
       |   |   |   |   | ... |
       |    2バイトずつ10個連続    |
       |
      500番地

上の図では short 型の i は500番地にあり、その番地から2バイトずつ10個、即ち20バイト連続してメモリー上に確保されることになります。

 定義した配列の各要素は上の図のものでは、

 i[0] が500番地にある要素で配列 i の最初 の要素になります。
 i[1] は502番地にある要素で配列 i の2番目の要素になります。
 i[9] は518番地にある要素で配列 i の最後 の要素になります。

この配列の [ ] 内の値を配列の添え字といいますが、C言語の不自然なのがこの添え字です。人間は物を数えるときには1、2、3... と1から数えていきますが、C言語では0、1、2... と0から数えるのです。もっともこれも慣れてしまえばあまり気にならなくなるのですが、コンピュータにとってみればこの方が自然なのです。どうしてこのようになってしまったのかというと、後にポインタについてお話ししますが、ポインタは、

 int i[10],*p;

 p=i;

としたときに、

*p と *(p+0) が同じ配列の要素(配列の最初の要素)を示すことから、C言語全体の統一をとるために配列での表現も添え字は0から開始することにしたのだと考えられます。

int i[10];

と配列を定義したとき、i[0] は配列 i の0番目の要素の値を意味しますが、添え字を付けない i は配列 i のアドレス(配列 i の最初の要素のアドレス)を示します。

int j[10][5];

と定義したものは、j[4][3] は値を示しますが、j[4] はアドレス、j もアドレスを示します。このようにC言語は配列の要素そのものを指定したときにはその値を、配列を指定したときにはアドレスと解釈されるのです。ですので、この場合の j[4] は5個の要素を持つ配列になるので、アドレスが値となることになるのです。これもC言語の大きな約束事ですので覚えておいて下さい。

 ここで話しを文字列の定数に戻しましょう。文字列の定数というのは、定数ではありますが、固定の文字の連続したものなので文字の配列と考えることができるのです。ということは、文字列そのものの値はアドレスということになるので、前回説明した内容に一致するのです。

 次に変数の初期化についてお話ししましょう。C言語の変数はK&R仕様のコンパイラでは変数の定義と同時にその内容を任意の値で初期化することは変数の性質によってはできるものとできないものがありました(ここで言う変数の性質とは、スコープの違いや、寿命の違いのことで今回は説明しません)。ANSI仕様のC言語では全ての変数が定義と同時に初期化することができるようになりました。

 int  i=10;
 double d=5.5;

と記述すると、整数の変数 i は定義と同時に値 10 で初期化され、実数の d は定義と同時に値 5.5 で初期化されます。多分ここまでは問題はないと思います。問題は配列の初期化です。配列の初期化は次のように { } の中にコンマで区切って記述します。

 int i[10]={1,2,3,4,5,6,7,8,9,10};

とすれば、最初の要素に1、2番目の要素に2 と順番に設定されます。

次に、

 int i[10]={1,2};

とすると、

 最初の要素は1、2番目の要素は2になり、それ以降は0になります。

 ところが、

 int i[10];

では、変数の性質によっては0か不定のどちらかになるのです。更に、初期化するのであれば、次のようにも記述することができます。

 int i[]={1,2,3,4,5};

このようにすると、コンパイラは配列の要素数が5個であると判断し、正しくコンパイルしてくれます。しかし、

 int i[5]={1,2,3,4,5,6};

と配列の要素数よりも初期化する値の数の方が大きいときにはコンパイルエラーになってしまいます。

 文字配列のときにも同様で、

 char c[3]={'a','b','c'};

と記述できますが、文字配列を文字列として扱うときには文字列の最後を示すためにヌルが必要になりますので、

 char c[4]={'a','b','c','\0'};

と記述しなければなりません。しかしながら、文字列で初期化するにはこれでは面倒なので、

 char a[]="abc";

と記述することで、コンパイラは配列 a の要素数を4として文字列 abc で初期化するようになります。多分これは文字列の初期化のみの例外だと思われます。

 それではまた次回。


そのお悩み、
アポロ技研に話してみませんか?

アポロ技研でワンランク上の物創りへ!
そのお悩み、アポロ技研に話してみませんか?