ログイン
kid's world

データ型と変数

前章で述べたように、表示される計算結果が、時々意図していた結果と異なる場合があります。これにはしっかりとした理由があります。本章では、なぜそのようなことが起きるのか、どのようにすれば意図した答えが得られるのか、これを理解するためにC++言語で基本中の基本となる、データ型と変数について解説します。


小数点以下が表示されない?

#include <iostream>
using namespace std;

void main()
{
  cout << 10 / 3 << endl;
}

これは、前章で書いたプログラムです。「10÷3」を計算したかったのですが、肝心の答えは「3」となってしまい、小数点以下が表示されません。

これはどういうことかというと、以前にちらっと出てきた「データ型」と深い関係があります。コンパイラは「10」と「3」はという数値を、

「整数型」

と判断し、答えも整数値として表示してしまいます。これは少々厄介な問題ですが、この問題をしっかりと理解すれば、後々のミスを防ぐことができます。このあたりの話を少し突っ込んでしてみましょう。

データ型と算術変換

C++言語には「データ型」という概念があります。データ型とは、その名のとおり、データがどのような形をしているか、つまりそれは「整数」であるのか、「実数値」であるのか、「文字」であるのか、はたまた「はい」か「いいえ」というデータなのか、ということです。

データ型にあまりこだわらないプログラミング言語も存在しますが、データ型を持つことによって、厳密なプログラム、効率的なプログラムを作ることができます。データ型には、それぞれ固有の名前がついています。C++言語のデータ型のうち、主要なものを以下に示します。

種類データ型サイズ
論理型bool1バイト
文字型char1バイト
整数型short2バイト
int4バイト
long4バイト
実数型float4バイト
double8バイト

(「サイズ」はBorland C++ 5.5の場合)

論理型の「bool」というのは、「はい」「いいえ」を表すデータ型です。文字型の「char」は、半角英数字である「A」や「@」などを表すデータ型です。以下同様に、整数型の「short」「int」「long」は整数値を、実数型(浮動小数点型とも言う)の「float」「double」は、小数点以下まで扱える実数値を表すデータ型です。

小数点以下を表示するには?

では、もう一度、先ほどの問題のプログラムを見てみましょう。

#include <iostream>
using namespace std;

void main()
{
  cout << 10 / 3 << endl;
}

割り算を行う記号、除算演算子「/」をはさむようにして、「10」と「3」という数値がかかれていますが、実はコンパイラはこれらを

整数値である、つまりint型である

と認識してしまいます。除算演算子「/」は、

与えられた数値の型で演算を行い、計算結果はその型で返す

という性質を持っています。「10」や「3」はint型ですので、計算結果もint型となり、それを受け取ったcoutは、結果として「3」という整数値を表示することになります。つまり簡単に言ってしまえば、

整数値を渡されたから、答えは整数値にした

ということです。

[図 04-01:整数値を返した除算演算子]

では、これらの数値を「実数値である」とコンパイラに認識させるにはどうしたらよいのでしょうか。この問題を解決したのが、次のプログラムです。

#include <iostream>
using namespace std;

void main()
{
  cout << 10.0 / 3.0 << endl;
}

実行結果:

3.33333


以前のプログラムとの違いは、「10」が「10.0」に、「3」が「3.0」になっただけです。実はコンパイラは、

数値に小数点がつくと、double型として認識する

のです。除算演算子「/」はdouble型の値を受け取ったので、計算結果の値もdouble型で返します。結果として、このプログラムを実行すると小数点以下まで表示されるようになります。

このように、プログラム中において値が変化しない要素を「定数」と呼びます。定数がどのようなデータ型であるかは、厳密に定められており、「小数点がつくとdouble型になる」というのは、その一例です。


note:

値が変化しない要素、つまりint型の「10」や「3」、double型の「10.0」などは、「定数」や「リテラル」と呼ばれます。定数にはいろいろな種類があります。ここで、数値に関する定数、「整数定数」と「浮動小数点定数」について解説します。

整数定数

整数定数は、その名のとおり、整数の定数です。8進数、10進数、16進数による表現などもできます。8進数は、最初に0がつきます。その後は、0,1,2,3,4,5,6,7のいずれかが続きます。例えば8進数で「123」は、

 0123

と書きます。0123は10進数では83です。これを確認するために、coutを使って、10進数で表示してみます。

#include <iostream>
using namespace std;

void main()
{
  cout << 0123 << endl;
}

実行結果:

83

10進数は、表現するために特別な記号を指定する必要はありません。最初の一文字は1, 2, 3, 4, 5, 6, 7, 8, 9のいずれかで書き、その後は0, 1, 2, 3, 4, 5, 6, 7, 8, 9のいずれかを使用して続けて書きます。例えば、次のとおりです。

1  12  120

16進数は、はじめに「0x」もしくは「0X」で始まります。その後は、0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, A, B, C, D, E, Fのいずれかを使用して続けて書きます。A, B, C, D, E, Fは、それぞれ10進数で10, 11, 12, 13, 14, 15に相当する値を表すために使われます。大文字と小文字の区別はありません。

例えば、16進数のFFを定数として書くには、

0XFF

とします。8進数のときと同じように、coutで表示させて10進数ではなにに相当するのかを確認してみましょう。

#include <iostream>
using namespace std;

void main()
{
  cout << 0XFF << endl;
}

実行結果:

255

浮動小数点定数

浮動小数点定数の型は、doubleになります。以下は、浮動小数点定数の例です。

3.14    0.14    3.0    .14    3.    1.23e10    1.23e-10

見て分かるように、浮動小数点定数には必ず「.」が付きます。つまり、「.」をつければ、その定数の型はdoubleになるわけです。例の最後の二つは指数表現(10のべき乗)です。「e」もしくは「E」の後に続けて、数字列を書くことによって、1.23×1010や、1.23×10-10という表現をすることができます。


算術変換

もしかしたら、ちょっと意地悪な方は(私も含めてですが)、こんなプログラムを作ったらどうなるかと考えたかもしれません。

#include <iostream>
using namespace std;

void main()
{
  cout << 10 / 3.0 << endl;
}

コンパイラには「10」はint型、「3.0」はdouble型と認識されます。では、その間にはさまれた除算演算子「/」は、その計算結果をいったいどのような型で返すのでしょうか。答えを知るには、このプログラムを実行してみるのが一番です。どうなりましたか?


実行結果:

3.33333


前のプログラムと変わらず、「3.33333」と小数点以下まで表示されました。どうやら演算結果はdouble型として返されたようです。

実は、異なる型のデータ同士を演算しようとすると、よりたくさんの表現ができる型に自動的に統一されて、その上で演算が行われます。これを算術変換といいます。

この例では、int型よりもdouble型のほうが広い表現ができる型ですので、まず「10」がdouble型に変換され、その上で除算が行われます。よって結果もdouble型となったわけです。

[図 04-02: 算術変換]


note:

実際には算術変換の法則は厳密に定義されていますが、全て「より広い表現のできる型に変換する」というポリシーは変わりませんので、そのように理解してください。


変数、データを保存しておく箱

なぜわざわざデータ型という概念をここまで詳しく説明したのか。これには割り算が正しく行われないということを解決するため以外の目的があったからです。すなわち、今後プログラムを作っていく中で必ず必要となる「変数」を理解するためにも必要な概念だからです。

ここで新しく出てきたキーワード「変数」とは、数値や文字などのデータを一時的に保存しておく箱のようなものです。

[図 04-03: 変数はデータを保存する箱]

なぜ変数が必要なのでしょうか? それは明らかです。ここまでの解説で「画面に文字を表示する」ことや「計算をする」ということは学びましたが、「記憶をする」という事は学んでいません。私達人間にとっても「記憶」は大変重要なものです。コンピュータにとっての記憶、それこそが変数なのです。

変数は、

  データ型

  名前

を持ちます。データ型は、その変数にどのようなデータが入るのかを決めるのに必要であり、名前は、いくつも存在する変数を識別するために必要です。

[図 04-04: 変数の名前とデータ型]

変数の宣言

前置きはこのぐらいにして、実際に変数を使ってみましょう。変数を使うには、必ず使う前に「変数宣言」を行なわなければなりません。言い換えれば、このプログラムの世界にデータを詰め込むことの出来る箱を用意するということです。どのような名前の、どのような箱を用意するのか、これを明らかにするのが「変数の宣言」です。宣言は、以下の文によって行ないます。

型 名前;

note:

変数には、自由に名前をつけることができますが、つけられない名前も存在します。

変数名には、英数字とアンダースコア「_」を使うことが出来ます。ただし、数字で始まる変数は作れません。つまり、「variable」や「var1」は変数名として使えますが、「1var」は変数名としては使えません。また、C++のキーワードも変数名として使うことができません。C++のキーワードを以下に全て列挙します。

and,and_eq,asm,auto,bitand,bitor,bool,break,case,catch,char,class,compl,const,const_cast,continue,default,delete,do,double,dynamic_cast,else,enum,explicit,extern,false,float,for,friend,goto,if,inline,int,long,mutable,namespace,new,not,not_eq,operator,or,or_eq,private,protected,public,register,reinterpret_cast,return,short,signed,sizeof,static,static_cast,struct,switch,template,this,throw,true,try,typedef,typeid,typename,union,unsigned,using,virtual,void,volatile,wchar_t,while,xor,xor_eq

これは変数名だけではなく関数名など、一般的にC++言語のプログラム上で名前を宣言する場合に当てはまります。


例えば「i」という名前の、int型を扱う変数を宣言するには、

int i;

という宣言文を書きます。この文を書くことによって、これ以降では、int型のデータを記憶できるiという名前の変数が使えるようになります。この文を書く位置は非常に重要です。通常、変数は関数の中で宣言します。関数の中でこの文を書くと、その関数のそれ以降の文から関数の終わりまで、「i」という変数が使用できるようになります。この変数が使える範囲のことを「スコープ」と呼びます。

[図 04-05: 変数のスコープ]

変数にデータを入れる

さて、変数というデータを入れる箱は用意しましたが、この中に実際にデータを入れるにはどうしたらよいのかを解説します。「変数iに10を入れなさい」という意味の英語を用いた、何か特別な命令があるわけではありません。おそらくみなさんは、次の文を見てあっけなく思われるでしょう。

i = 10;

これが、「変数iに10を入れる」という操作を行う文です。このイコール「=」を「代入演算子」と呼び、この操作を代入と呼びます。数学の等式みたいに見えますね。しかし、そう思ったら大間違いですので、気を付けてください。

もちろん、この文を実行したところで、画面上には何の変化もおきません。しかし、確実にコンピュータに用意されたiという変数には、10という数値が記憶されました。

変数の中身を取り出すには

変数に値が記憶されたと言っても、その証拠が見て取れないのでは、少々不安ですね。そこで、変数の中身を調べる方法を解説します。とはいっても、それはものすごく簡単なことです。今まで、数字、つまり定数を表示するのにcoutを使ってきました。例えば次のような文です。

cout << 10 << endl;

変数も、定数と同じようにcoutで表示することができます。今まで定数で書かれていた部分を、変数に置き換えるだけです。

cout << i << endl;

では、ここまでの知識で、変数を使った簡単なプログラムを一つ作ってみましょう。と言っても、今まで書いてきた文をただ並べただけですが。

#include <iostream>
using namespace std;

void main()
{
  int i;
  i = 10;
  cout << i << endl;
}

実行結果:

10

いかがでしたか? このプログラムは、変数の宣言と、変数への代入と、変数の値の利用を示した最も単純なプログラムです。意外と簡単だったのではないでしょうか?

変数の宣言時の初期化

このプログラムのように、必ず変数の初期値が決まっている場合は、

  変数を宣言するときに初期値を与えることによってその変数を初期化する

ことができます。これを変数の初期化といいます。この例のように、10で初期化されたint型変数iを宣言するには、変数名の後に「=」を書き、つづけて初期値を与え、

int i = 10;

と書きます。よって、先ほどのプログラムは、

#include <iostream>
using namespace std;

void main()
{
  int i = 10;
  cout << i << endl;
}

と書いても同じ動きをするようになります。ただし、これは代入ではなく変数の初期化ですので、今はあまり気にしなくてもいいのですが、少々意味合いが異なってきます。

複数の変数を同時に宣言する

型が同じ変数であれば、

int a;
int b;
int c;

と宣言しなくても、コンマ「,」で区切って、

int a, b, c;

と、一行で宣言することができます。もちろん、初期化も行うことができます。

int a=10, b=20, c;

計算結果を代入する

単なる数値の代入ではなく、計算結果を代入することもできます。例えば、

i = 10 + 20;

という文は、変数iに30という値を代入します。また、もう一つ別のint型変数jが宣言されていれば、

i = 10 + 20;
j = i + 30;

という書き方ができます。変数は定数と同じように振舞いますので、iの値、つまり30と30が足しあわされて、60という値がjに代入されます。

代入演算子のいろいろ

代入の記号がイコール「=」なので、代入を行う文は数学の等式のようにも見えますが、以下のような文もあたりまえのように成り立つということに注意してください。ここまで説明したことだけを忠実に理解していれば、分かるはずです。

i = i + 10;

これは数学の等式ではありえない式ですが、この文は等式ではなく「代入」ですので、正しい文です。例えばiに10という数値が入っていたとすると、

i = 10 + 10;

と考えることができます。10+10は20ですから、

i = 20;

と同じ意味になります。このような式はC++言語のプログラムで良く使われるスタイルですので、特別に次のような書き方が許されています。

i += 10;

これを見ても分かるように、「i = i + 10;」と「i += 10;」、この2つの文はどちらも

変数iの中身を10増やす

という意味になります。この「+=」のような演算子も代入演算子と呼ばれ、主に以下のようなものがあります。

+=
-=
*=
/=
%=

さらに、加算減算に関しては、足す数、引く数が1の場合に限り、それぞれ以下のような表記が可能です。

i++;
i--;

これらをそれぞれ、インクリメント演算子、デクリメント演算子と言います。つまり、

i = i + 1;
i += 1;
i++;

の3つの文は、全て同じ結果になります。

インクリメント演算子、デクリメント演算子は、次のような書き方もできます。

++i;
--i;

変数の前に置かれる演算子なのでそれぞれ「前置インクリメント演算子」や「前置デクリメント演算子」と呼ばれます。それに対して変数の後に「++」や「--」を置いたインクリメント、デクリメント演算子を「後置インクリメント演算子」や「後置デクリメント演算子」と呼びます。「i++」と「++i」がどう違うのかは、後ほど詳しく説明します。

まとめ

キーワード

広告


©Toshio Koide 1996-2007.

目次

リンクについて

リンクは御自由にどうぞ。

メール

mail.gif

広告