変数とはプログラムの中で使用する数の入れ物の事です。
a,x,iなどですが、これは何でも良いというものではなく、用途によってタイプが変わります。
始めは数をいれる変数を考えます。
これは主にビット数によってタイプが変わります。
8ビットの変数を byte といいます。
前に説明したように8ビットですから255までしか数えられません。
最高で40人しかいない学校のクラスの生徒数をいれるのには便利です。
同じく8ビットの char があります。
8ビットの変数にはキャラクタをいれることができます。
キャラクタとは a とか、b とかの文字の事です。
そのためか char をキャラと書いてある本がありますが、やはり「ちゃー」と読むのが正統ではないでしょうか。どちらでもいいんですけど。
その他どんなものがあるか、列挙してみましょう。
int main()
{
byte a; 8ビット 1バイト バイト型
char b; 8ビット 1バイト 文字型
int b; 16ビット 2バイト 整数型
long c; 32ビット 4バイト 倍長整数型
double e; 64ビット 8バイト 実数型
}
宣言をするとその変数が使えるようになります。
ただし、使えるようになるだけで中身はなにも入っていません。
ですから、中になにかをいれてから使わないとおかしな事になります。
なかに数をいれることを定義する、といいます。
int a;
とすると、a は使えますが、中身は何が入っているかわかりません。
そこで
a = 0;
と定義してから使います。
int a=0;
という風に宣言と定義を同時にすることも出来ます。
定義のし忘れは、このような整数なら正常に動かないくらいでたいした害がありませんがポインタ変数を定義しわすれると悲惨な結果を招くので注意しましょう。
重大な結果とは、ウィンドウズがハングアップしてソースが消える、などです。
ポインタについては後で説明します。
ENUMによる定義
enum を使うと自分で決めた定数を決めることができます。
enum は日本語では列挙型といいます。
使える名前を列挙して、それ以外は使えません。
たとえば、
// enum キーワードの例
enum Joutai // enum による キャラクタ状態型 定義
{
genki, // 元気
sleep, // 睡眠
poison, // 毒
knockout, // KO
dead, // 死
ash, // 灰
loat // LOST
};
Joutai p1joutai ;
デフォルトでは、 genki から0、1、、2、3、4、5、6という数が対応します。
これで joutaui 型の変数を定義しました。
今後は joutai 型変数は、上の七個の状態名以外の数をいれることは出来ません。
Joutai Player1Joutai; // プレイヤ1の状態を保持する変数
Player1Joutai = genki;
if (Player1Joutai == genki){
cout << "元気です";
}
この場合は p1joutai は、genki〜lost までの数(?)しか入れられません。
それは int 型変数には、 intの数しか入れられないのと同じです。
enum の使用法としては、状態を表したりするとにに使うようです。
あるいは曜日、ランク、階級などにも応用できそうです。
bool boolean ブーリアン変数といいます。
中に入れられるのは true と false の2値です。
実際にはint の数が割り当てられるようですが、使うのは true, false だけです。
true と false とは、下のような関係になっています。
TRUE = 1 = YES=正しい
FALSE = 0 = NO=間違っている
if (...) のカッコの中に入るのは、式でも (例 aka == 3, a <= 45, a >=
b ) 数でも良いのですが
実際にやってみると、カッコの中が 0 のときは FALSE と同じく動いてそれ以外の数のとき
(例 if ( 4) )はTRUE として動くようです。
1 == 1 は、1と1を比較していますが、この結果は、正しいので TRUE
この事を 1==1という式の値は、TRUEである、といいます。
if 文や、while 文などに使う条件は、このようにYESか、NOの値をもつ式か、数になります。
int a として、
a = 1 のとき、
a == 1 の値は、 TRUE
a == 2 の値は、FALSE
bool aka;
aka = true;
if (aka == true){
cout << "aka はTRUEです";
}
構造体 構造体とは読んで字のごとく、構造を持っている変数です。
構造変数と言った方がわかりやすいかもしれません。
構造体でよく引き合いに出されるのが社員名簿です。
社員名簿には社員番号、入社年月日、性別、住所、基本給、年次休暇など、いろいろな要素が含まれています。
それを一括して扱えるように定義できます。
ここでは、ゲームのプログラムということで、パーティのキャラクタの構造体を考えてみましょう。
struct partychara{
char *name; // 個人名(ポインタ)
int trib; // 種族 0-12 まである
int age; // 年齢
int level; // レベル
int HP; // ヒットポイント
int MP; // 魔法ポイント
int buki; // 武器
int bougu; // 防具
int biits; // ブーツ
int item[8]; // アイテム(8個持てる)
};
partychara momotaro; // ここでキャラを定義する
momotaro.name = "桃屋桃太郎"; // 名前を文字列でいれる
momotaro.trib = 1;
momotaro.age = 16;
実際に使用するときはゲームが始まってから終わるまで保持するデータなのでグローバル変数にするか、クラスの中に定数として持つようにします。
またこの構造体はあとで説明するクラスの元になる考え方でもあるので使い方をマスターしてください。
構造体が普通に使えるとクラスの理解も比較的スムーズにゆくでしょう。
ポインタ
ポインタをいうのはその名の通り、ポイントするひと、すなわち「指し示す」人あるいはものです。
中味は単なる数値ですが、それはアドレスを指し示しています。
今 int a;
と変数を宣言します。そして
a = 100;
とします。
すると、a という変数の中には100という数が入りますね。
そのa という変数は、CPUのアドレス空間のどこかに存在しています。
CPUのアドレスの説明を思い出して下さい。
アドレスの始めのほうはBIOSで、次がウィンドウズですね。
そのあとできっとVC++があって、その後に自分の書いたプログラムがあるはずです。
つまり、プログラムは、アドレスに数字がずらずらと入っているものでしたね。
実際にCDや、ハードディスクからプログラムを読み込むとメモリ上に
アドレス
|
データ
|
10000番地〜12000番地
|
プログラム
|
12001番地〜12300番地
|
定数
|
12301番地〜12600番地
|
変数
|
|
のように読み込みます。
先ほどの int a;
ですが、実際にはこの a は、たとえば12301番地の中味をさします。
a = a + 1 ;
という式は、12301番地のなかにある数値を +1 する、という意味です。
ニーモニックを見ると、そのようにコンパイルされています。
このときに、 a という変数を使う人は、 12301番地、あるいは、12301 という数は
使う必要もないし、通常は見えません。
ですが、論理的に a という変数の中味をあらわすためには、プログラム上で「 a 」 と
表現する方法と、「12301番地の内容」と表現する方法があります。
このとき、12301番地の12301という番地(数値)を入れるための変数がポインタ変数です。
int *p;
p = &i;
とすると、pに 12301という数値が入ります。
& は、 i のアドレスを取り出す記号です。
&i は、変数 i のアドレスという意味です。
ここで、p = p+1 という式を考えてみます。
すると、ポインタ p は、何をさすのでしょうか。
答えは、「わかりません」です。
もちろん、12302番地の内容ですが、それがなにかはわからないのですね。
もし、それが配列なら、
int i[20]; // i は int 型の20個の配列
p は i[0]
p = p +1 としたあとは
p は i[1] になります。
ポインタは何の役にたつのでしょうか?
今はわかりませんが、あらゆる場面のあらゆる場所で使われますので、その都度説明しましょう。
int *pz; // pz という名前のポインタを宣言する
int s; // s という名前のint 型変数
s = 1000; // s の値を定義する
cout << s << "\n"; // s の中味を確認する
pz = &s; // pz に s のポインタを設定する(sのアドレスをいれる)
*pz = 65; // pz の指し示す場所(つまり s )に65をいれる
cout << s << "\n"; // s の中味が65になっているのを確認する
プログラムもメモリの中にあるということは、関数の先頭もアドレスを持っている
ということになります。
ということは、関数もポインタを持てるということになります。
関数自体をポインタで表現すると、どうなるんでしょうか。
関数をポインタで呼び出せることになります。
なんだか面倒そうですが、やり方は簡単です。
int disp123() // 通常の関数を定義する
{
cout << "123";
}
int main()
{
int (*function)(); // ファンクション(関数)をいれるポインタ=function を宣言する
function = disp123; // 関数ポインタに disp123 という関数のポインタをセットする
(*function)(); // ポインタを関数として実行することで disp123 を呼び出す
}
関数のポインタもあとで説明するクラスに深く関わっているので覚えておいて下さい。
配列
配列は、変数や定数をまとめて扱うために使います。
今まで説明した変数や定数はすべて配列として扱うことができます。
配列は以下のように宣言します。
int ttta[13]; // int 型の変数が13個ならんでいる。名前は ttta
この変数の使い方は、
ttta[0] = 0;
というように使います。ttta[0] は 13個並んだ int 型変数の0番めという意味です。
ttta は全部で13個なので、ttta[0] から ttta[12] まであります。
ここで ttta[13] = 0; などとして実行すると、「このプログラムは不正な処理・・・」のエラーを起こします。
ttta[13] は一列に並んだ配列ですが、一列に並んだ配列がさらに何本も並んでいる
2次元配列を作ることもできます。
書き方は、
tttb[100][13];
です。
これは13個並んだ int 型の配列が横に(縦でも良いですが)100本並んだ配列です。
同じく3次元 tttc[4][100][13] 4次元 tttd[5][4][100][13] といくらでも定義できます。
ただ余り次元の数が多い場合はデータ構造の設計ミスの可能性が高いので実用的にはせいぜい3次元配列くらいに収めるのが妥当でしょう。
構造体も配列にできます。
先ほどの partychara ですが、使うときにはもちろん配列にしてパーティの中に6人いると決めて配列でアクセスできるようにします。
struct partychara{
char *name; // 個人名(ポインタ)
int trib; // 種族 0-12 まである
int age; // 年齢
int level; // レベル
int HP; // ヒットポイント
int MP; // 魔法ポイント
int buki; // 武器
int bougu; // 防具
int biits; // ブーツ
int item[8]; // アイテム(8個持てる)
};
partychara momotaro[6]; // ここでキャラを定義する
momotaro[0].name = "桃屋桃太郎"; // 名前を文字列でいれる
momotaro[0].trib = 1;
momotaro[0].age = 16;
これでパーティは一人ではなく6人になりました。
bool flagarry[100];
これはブーリアンの配列です。
どういう場合に有効なのか、ちょっそ想像できないんですが。
次は関数をいれるポインタの配列です。
int (*function[10])(); // ファンクション(関数)をいれるポインタ=function を宣言する
10個の関数のポインタをいれることができます。
関数のポインタ配列はトリッキーですが、読みやすくかっこいいソースを書くのに有効です。
しかしこれは上級のテクニックになりますので無理に使う必要はありません。
ただ、他の人のソースを読むためとクラスの説明に必要なのでにこういう書き方もある、と覚えておいて下さい。
説明していないものには 共用体(UNION)がありますが、省略します。
使う用途がほとんど考えられません。
この機能はアセンブラ時代のテクニックを引きずっているように見えます。
ほかの機能で足りるので省略です。
宣言と定義を同時に行う
これまでの説明では変数の宣言と定義は別の部分で行っていました。
しかし、関数のポインタなど、いれる値がはじめから決まっているのなら宣言と定義を同時に行った方がわかりやすいですね。
ポインタに限らず、同時に定義する方法があります。
以下にならべてみましょう。
int a=0; // int
char a ='b'; // 文字型
char a[] = "1234hex_alex"; // 文字列
long a = 12345678; // long 型
int (*function)() = disp123; // ファンクション(関数)をいれるポインタ
int (*function[3])() = {disp123, disp456, disp789}; // ファンクション(関数)ポインタ
また変数配列を宣言と同時に初期化するには以下のように書きます。
int a[3][4] = { // 4個づつ並んだ配列が3本の場合
{0,1,2,3},
{2,3,4,5},
{4,5,6,7}
};
関数ポインタの場合は
int (*funcz() = disp123; // 関数ポインタ
int (*funcz[3])() = {disp123, disp456,disp789}; // 関数ポインタの配列
の様に定義します。
それぞれ引数付きの場合は以下のようになります。
int (*funcx)(int vv) = disp123;
int (*funcz[3])(int vv) = {disp123, disp456,disp789};
関数ポインタの法に引数を宣言するので、引数のタイプの違う関数をポインタ配列で定義することは出来ません。
その場合は違うポインタを使うしかありません。
変数というのはいろいろな値を書き入れたり読み出したりするものでいろいろな種類があるのがわかりました。
これとは別に変わっては困る値があります。
これを定数といいます。
変数を定数として使ってもよいのですが、間違って書き換えてしまったりすると面倒です。
そこで、はじめから終わりまで変わらない値を定数として定義します。
定数の宣言と定義の仕方は
static int a=0;
static char *name = "山田太一";
static int sttd[2] = { 0,1};
などです。
つまり始めに static というキーワードをつけるとこれは定数だぞ、ということになります。
最後に変数の使用で気をつけなければいけないことがあります。
それは、「スコープ」です。
日本語で言うと適応範囲という意味ですね。
以下のようなソースがあったとしましょう。
#include <iostream.h>
int hana=15;
int main()
{
kansu2();
cout << hana << "\n";
return 0;
}
void kansu2()
{
int hana=4;
cout << hana << "\n";
}
include の下にある int hana=5; はグローバル変数といいます。
この変数は main() や kansu2() の外で宣言されているので、どの関数からもアクセスすることができます。
さあ、このソースをよーく見てみましょう。
なにはおかしな所はありませんか?
そう、 hana という変数が2個所で定義されています。
これはエラーにならないのでしょうか?
コンパイルではエラーになりません。
実行もできます。
では実行するとどうなるでしょうか。
結果は
E:\myproject\test\ddd>
4
15
Press any key to continue
となります。
void kansu2()
{
int hana=4;
cout << hana << endl;
}
この中で cout に出る hana はこの kansu2() の中にある hana ですね。
一方、main() の中で使っている hana は、main() の中にはないのでこれはグローバル変数の hana が使われるわけです。
では、kansu2() の中でグローバル変数を使うことは出来るでしょうか?
それは
cout << ::hana << "\n";
の様にかけばOKです。
:: はスコープ解決演算子といいます。
これは変数や関数がどこに属するか、どこから見えるのか、を指定しているわけです。
通常みるのは
button::onpush();
というようにクラスに属するメンバ関数などですね。
これはbutton くらすに属する onpush() という意味です。
上の
::hana は、::の左側に何もありませんが、こういうときはファイルに属する、つまりグローバル関数ということになります。
次は演算子(オペレータ)の説明です。
演算子とは演算をするための記号です。
普通思い浮かぶのは +(プラス) -(マイナス) *(掛ける) /(割る)ですが、
C++の場合は他にもたくさんあります。
用語の使い方は以下の様になりまいす。
左辺 演算子 右辺
a = 123;
列挙します。
演算子
|
意味
|
使用例
|
動作
|
==
|
比較して同じなら true |
if (a == b) {.....} |
aがbと等しければ{...}を実行する |
!=
|
比較して違っていたら true |
if ( a != 12){ ... } |
aが12と違っていれば{...}を実行する |
<
|
左辺より右辺が大きければ true |
if ( a < 34){...} |
aが34より小さければ{...}を実行する |
>
|
左辺より右辺が小さければ true |
if ( a > c){...} |
aがcより大きければ{...}を実行する |
<=
|
左辺が右辺より小さいか同じなら true |
if ( a <= 34){ ...} |
aが34以下なら {...}を実行する |
>=
|
左辺が右辺より大きいか同じなら true |
if ( a >= b){...} |
a がb と同じか、大きければ {...}を実行する |
=
|
右辺の値を左辺に代入する |
a = 4 |
a に4を入れる |
-
|
減算 |
|
|
+
|
加算 |
|
|
*
|
乗算 |
|
|
/
|
除算 |
|
|
%
|
剰余(割った余り) |
a = b % 6; |
bを6で割った余りを a に入れる |
&&
|
条件のアンド |
if ( a > 123 && a <500){...} |
aが123より大きく、500より小さい場合は{...}を実行する |
||
|
条件のオア |
if (a==0 || a==15){...} |
aが0、あるいはaが15のときは{...}を実行する |
>> <<
|
やじるし方向のビットシフト |
a = a << 5 |
2進法で表現すると、a = 0000 0100(4) が 0100 0000(40H) になる
全体が矢印方向にずれる |
~
|
ビットの反転 |
a = ~a |
2進行で表現すると、 a= 0011 1010(3AH)が
1100 0101(0C5H) になる
0が1に、1が0になる |
&
|
ビットのアンド |
a = a & 8 |
a が(1111 1111)のとき( 0000 1000 ) とアンドすると、結果は
( 0000 1000 )
両方1のビットだけが1になる |
|
|
ビットのオア |
|
a が(1111 0000)のとき( 0000 1000 ) とオアすると、結果は (
1111 1000 )
どちらかのビットが1なら結果のビットも1になる |
^
|
エクスクルーシブオア |
a = a ^ 0xFF; |
a が 1010 1111 のとき (1111 1111)と、エクスクルーシブオアをすると、結果は(0101
0000)
両方のビットが違っている場合のみ1になる |
+=
|
足して代入 |
a += 5; |
a に5を足す a = a +5 と同じ |
-=
|
引いて代入 |
a -= 5; |
a から 5を引く a = a -5 と同じ |
*=
|
かけて代入 |
A *= 45; |
a に45を掛ける a = a *45 と同じ |
/=
|
割って代入 |
a /= 12; |
a を12で割る a = a / 12 と同じ |
%=
|
剰余を代入 |
a %=6; |
a を6で割った余りを a に入れる |
^=
|
エクスクルーシブオアを代入 |
a ^= 6; |
a と 6をエクスクルーシブオアした結果をa に入れる
a = a ^6; と同じ |
&=
|
ビットを&して代入 |
a &= 78; |
a = a & 78 と同じ |
|=
|
ビットをオアして代入 |
a |= 55; |
a = a | 5 と同じ |
<<=
|
左にシフトして代入 |
a <<= 4; |
a を左に4回シフトする a = a << 4と同じ |
>>=
|
右にシフトして代入 |
a >>= 5; |
aを右に5回シフトする a = a >> 5 と同じ |
++
|
+1する |
a ++; |
a = a +1 、 a += 1 と同じ |
--
|
−1する |
a --; |
a = a-1 、a -= 1 と同じ |
|